]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #91570 - nbdd0121:const_typeck, r=oli-obk
authorMatthias Krüger <matthias.krueger@famsik.de>
Wed, 8 Dec 2021 15:08:08 +0000 (16:08 +0100)
committerGitHub <noreply@github.com>
Wed, 8 Dec 2021 15:08:08 +0000 (16:08 +0100)
Evaluate inline const pat early and report error if too generic

Fix #90150

````@rustbot```` label: T-compiler F-inline_const

783 files changed:
Cargo.lock
compiler/rustc_ast_pretty/src/helpers.rs
compiler/rustc_ast_pretty/src/pprust/state.rs
compiler/rustc_codegen_gcc/src/asm.rs
compiler/rustc_codegen_llvm/src/asm.rs
compiler/rustc_const_eval/src/interpret/eval_context.rs
compiler/rustc_const_eval/src/interpret/operand.rs
compiler/rustc_const_eval/src/interpret/place.rs
compiler/rustc_const_eval/src/interpret/step.rs
compiler/rustc_const_eval/src/transform/check_consts/check.rs
compiler/rustc_const_eval/src/transform/check_consts/ops.rs
compiler/rustc_data_structures/src/binary_search_util/mod.rs
compiler/rustc_data_structures/src/graph/dominators/mod.rs
compiler/rustc_data_structures/src/graph/dominators/tests.rs
compiler/rustc_data_structures/src/graph/iterate/mod.rs
compiler/rustc_data_structures/src/graph/scc/mod.rs
compiler/rustc_data_structures/src/graph/vec_graph/mod.rs
compiler/rustc_data_structures/src/lib.rs
compiler/rustc_data_structures/src/sorted_map/index_map.rs
compiler/rustc_data_structures/src/sso/map.rs
compiler/rustc_data_structures/src/sso/set.rs
compiler/rustc_data_structures/src/vec_linked_list.rs
compiler/rustc_expand/src/config.rs
compiler/rustc_hir_pretty/src/lib.rs
compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
compiler/rustc_infer/src/infer/opaque_types.rs
compiler/rustc_interface/src/tests.rs
compiler/rustc_lint_defs/src/builtin.rs
compiler/rustc_llvm/build.rs
compiler/rustc_middle/src/mir/mod.rs
compiler/rustc_middle/src/mir/terminator.rs
compiler/rustc_middle/src/ty/instance.rs
compiler/rustc_middle/src/ty/normalize_erasing_regions.rs
compiler/rustc_mir_transform/src/add_retag.rs
compiler/rustc_mir_transform/src/check_const_item_mutation.rs
compiler/rustc_mir_transform/src/check_packed_ref.rs
compiler/rustc_mir_transform/src/check_unsafety.rs
compiler/rustc_mir_transform/src/const_debuginfo.rs
compiler/rustc_mir_transform/src/const_goto.rs
compiler/rustc_mir_transform/src/const_prop.rs
compiler/rustc_mir_transform/src/coverage/debug.rs
compiler/rustc_mir_transform/src/coverage/graph.rs
compiler/rustc_mir_transform/src/coverage/mod.rs
compiler/rustc_mir_transform/src/coverage/query.rs
compiler/rustc_mir_transform/src/coverage/spans.rs
compiler/rustc_mir_transform/src/coverage/tests.rs
compiler/rustc_mir_transform/src/deduplicate_blocks.rs
compiler/rustc_mir_transform/src/dest_prop.rs
compiler/rustc_mir_transform/src/early_otherwise_branch.rs
compiler/rustc_mir_transform/src/elaborate_drops.rs
compiler/rustc_mir_transform/src/function_item_references.rs
compiler/rustc_mir_transform/src/generator.rs
compiler/rustc_mir_transform/src/inline.rs
compiler/rustc_mir_transform/src/inline/cycle.rs
compiler/rustc_mir_transform/src/instcombine.rs
compiler/rustc_mir_transform/src/lib.rs
compiler/rustc_mir_transform/src/lower_intrinsics.rs
compiler/rustc_mir_transform/src/normalize_array_len.rs
compiler/rustc_mir_transform/src/nrvo.rs
compiler/rustc_mir_transform/src/pass_manager.rs
compiler/rustc_mir_transform/src/remove_uninit_drops.rs
compiler/rustc_mir_transform/src/required_consts.rs
compiler/rustc_mir_transform/src/separate_const_switch.rs
compiler/rustc_mir_transform/src/shim.rs
compiler/rustc_mir_transform/src/simplify.rs
compiler/rustc_mir_transform/src/simplify_branches.rs
compiler/rustc_mir_transform/src/simplify_comparison_integral.rs
compiler/rustc_mir_transform/src/simplify_try.rs
compiler/rustc_mir_transform/src/unreachable_prop.rs
compiler/rustc_parse/src/parser/attr.rs
compiler/rustc_parse/src/parser/diagnostics.rs
compiler/rustc_parse/src/parser/item.rs
compiler/rustc_parse/src/parser/mod.rs
compiler/rustc_parse/src/parser/stmt.rs
compiler/rustc_serialize/src/opaque.rs
compiler/rustc_session/src/config.rs
compiler/rustc_session/src/options.rs
compiler/rustc_session/src/session.rs
compiler/rustc_span/src/symbol.rs
compiler/rustc_target/src/asm/avr.rs [new file with mode: 0644]
compiler/rustc_target/src/asm/mod.rs
compiler/rustc_target/src/spec/m68k_unknown_linux_gnu.rs
compiler/rustc_target/src/spec/mod.rs
compiler/rustc_target/src/spec/riscv64gc_unknown_freebsd.rs [new file with mode: 0644]
compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
compiler/rustc_typeck/src/check/generator_interior.rs
compiler/rustc_typeck/src/check/method/suggest.rs
compiler/rustc_typeck/src/check/op.rs
compiler/rustc_typeck/src/collect.rs
compiler/rustc_typeck/src/collect/type_of.rs
library/alloc/src/collections/vec_deque/mod.rs
library/alloc/src/string.rs
library/alloc/src/vec/mod.rs
library/core/src/array/iter.rs
library/core/src/lib.rs
library/core/tests/array.rs
library/core/tests/simd.rs
library/portable-simd/CONTRIBUTING.md
library/portable-simd/README.md
library/portable-simd/crates/core_simd/examples/nbody.rs
library/portable-simd/crates/core_simd/src/comparisons.rs
library/portable-simd/crates/core_simd/src/lane_count.rs
library/portable-simd/crates/core_simd/src/masks.rs
library/portable-simd/crates/core_simd/src/masks/bitmask.rs
library/portable-simd/crates/core_simd/src/masks/full_masks.rs
library/portable-simd/crates/core_simd/src/math.rs
library/portable-simd/crates/core_simd/src/ops.rs
library/portable-simd/crates/core_simd/src/ops/assign.rs [new file with mode: 0644]
library/portable-simd/crates/core_simd/src/ops/deref.rs [new file with mode: 0644]
library/portable-simd/crates/core_simd/src/ops/unary.rs [new file with mode: 0644]
library/portable-simd/crates/core_simd/src/reduction.rs
library/portable-simd/crates/core_simd/src/select.rs
library/portable-simd/crates/core_simd/src/swizzle.rs
library/portable-simd/crates/core_simd/src/vector/float.rs
library/portable-simd/crates/core_simd/src/vector/ptr.rs
library/portable-simd/crates/core_simd/src/vendor/x86.rs
library/portable-simd/crates/core_simd/tests/autoderef.rs [new file with mode: 0644]
library/portable-simd/crates/core_simd/tests/ops_macros.rs
library/portable-simd/crates/test_helpers/src/lib.rs
library/std/src/ffi/mod.rs
library/std/src/os/raw/mod.rs
src/bootstrap/bootstrap.py
src/bootstrap/download-ci-llvm-stamp
src/bootstrap/native.rs
src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile
src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile
src/ci/docker/host-x86_64/dist-armhf-linux/Dockerfile
src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile
src/doc/book
src/doc/edition-guide
src/doc/nomicon
src/doc/reference
src/doc/rust-by-example
src/doc/rustc-dev-guide
src/doc/rustc/src/platform-support.md
src/doc/rustdoc/src/documentation-tests.md
src/doc/unstable-book/src/library-features/asm.md
src/librustdoc/html/markdown.rs
src/librustdoc/html/markdown/tests.rs
src/librustdoc/html/static/css/noscript.css
src/librustdoc/html/static/css/rustdoc.css
src/librustdoc/html/static/css/themes/ayu.css
src/librustdoc/html/static/css/themes/dark.css
src/librustdoc/html/static/css/themes/light.css
src/librustdoc/html/static/js/main.js
src/librustdoc/html/static/js/source-script.js
src/librustdoc/html/templates/page.html
src/test/assembly/asm/avr-modifiers.rs [new file with mode: 0644]
src/test/assembly/asm/avr-types.rs [new file with mode: 0644]
src/test/incremental/issue-85360-eval-obligation-ice.rs [new file with mode: 0644]
src/test/pretty/ast-stmt-expr-attr.rs
src/test/pretty/async.rs [new file with mode: 0644]
src/test/pretty/attr-derive.rs
src/test/pretty/auto-trait.rs
src/test/pretty/block-comment-trailing-whitespace2.rs
src/test/pretty/closure-reform-pretty.rs
src/test/pretty/disamb-stmt-expr.rs
src/test/pretty/enum-variant-vis.rs
src/test/pretty/example1.rs
src/test/pretty/example2.pp
src/test/pretty/example2.rs
src/test/pretty/expanded-and-path-remap-80832.pp
src/test/pretty/fn-return.rs
src/test/pretty/fn-types.rs
src/test/pretty/fn-variadic.rs
src/test/pretty/if-attr.rs
src/test/pretty/issue-12590-a.rs
src/test/pretty/issue-12590-c.pp
src/test/pretty/issue-12590-c.rs
src/test/pretty/issue-19077.rs
src/test/pretty/issue-30731.rs
src/test/pretty/issue-68710-field-attr-proc-mac-lost.rs
src/test/pretty/lifetime.rs
src/test/pretty/macro.rs
src/test/pretty/macro_rules.rs
src/test/pretty/nested-item-vis-defaultness.rs
src/test/pretty/path-type-bounds.rs
src/test/pretty/qpath-associated-type-bound.rs
src/test/pretty/stmt_expr_attributes.rs
src/test/pretty/tag-blank-lines.rs
src/test/pretty/trait-inner-attr.rs
src/test/pretty/trait-polarity.rs
src/test/pretty/trait-safety.rs
src/test/pretty/where-clauses.rs
src/test/rustdoc-gui/check-code-blocks-margin.goml
src/test/rustdoc-gui/escape-key.goml
src/test/rustdoc-gui/impl-default-expansion.goml
src/test/rustdoc-gui/search-result-go-to-first.goml
src/test/rustdoc-gui/source-code-page.goml
src/test/rustdoc-gui/toggle-docs.goml
src/test/rustdoc-gui/type-declation-overflow.goml
src/test/rustdoc/doc-cfg.rs
src/test/rustdoc/duplicate_impls/issue-33054.rs
src/test/rustdoc/impl-everywhere.rs
src/test/rustdoc/intra-doc/prim-methods-external-core.rs
src/test/rustdoc/intra-doc/prim-methods-local.rs
src/test/rustdoc/intra-doc/prim-methods.rs
src/test/rustdoc/intra-doc/true-false.rs
src/test/rustdoc/issue-55364.rs
src/test/rustdoc/keyword.rs
src/test/rustdoc/struct-implementations-title.rs
src/test/rustdoc/trait_alias.rs
src/test/rustdoc/tuple-struct-fields-doc.rs
src/test/ui/async-await/issues/issue-54752-async-block.rs
src/test/ui/async-await/issues/issue-54752-async-block.stderr
src/test/ui/async-await/issues/issue-60674.stdout
src/test/ui/attributes/issue-90873.stderr
src/test/ui/cfg/auxiliary/crate-attributes-using-cfg_attr.rs [deleted file]
src/test/ui/cfg/crate-attributes-using-cfg_attr.rs [deleted file]
src/test/ui/cfg/future-compat-crate-attributes-using-cfg_attr.rs [new file with mode: 0644]
src/test/ui/cfg/future-compat-crate-attributes-using-cfg_attr.stderr [new file with mode: 0644]
src/test/ui/const-generics/defaults/doesnt_infer.stderr
src/test/ui/const-generics/defaults/pretty-printing-ast.stdout
src/test/ui/const-generics/generic_const_exprs/closures.stderr
src/test/ui/const-generics/issues/issue-72845.rs [new file with mode: 0644]
src/test/ui/const-generics/issues/issue-72845.stderr [new file with mode: 0644]
src/test/ui/const-generics/issues/issue-90455.rs [new file with mode: 0644]
src/test/ui/const-generics/issues/issue-90455.stderr [new file with mode: 0644]
src/test/ui/consts/const-eval/const-eval-query-stack.stderr
src/test/ui/consts/issue-90870.fixed [new file with mode: 0644]
src/test/ui/consts/issue-90870.rs [new file with mode: 0644]
src/test/ui/consts/issue-90870.stderr [new file with mode: 0644]
src/test/ui/fn/fn-compare-mismatch.stderr
src/test/ui/fn/fn-recover-return-sign2.rs
src/test/ui/fn/fn-recover-return-sign2.stderr
src/test/ui/hygiene/unpretty-debug.stdout
src/test/ui/inference/erase-type-params-in-label.rs [new file with mode: 0644]
src/test/ui/inference/erase-type-params-in-label.stderr [new file with mode: 0644]
src/test/ui/inference/issue-83606.stderr
src/test/ui/issues/issue-59488.stderr
src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr
src/test/ui/lint/future-incompat-test.rs
src/test/ui/lint/issue-87308.stdout
src/test/ui/macros/nonterminal-matching.rs
src/test/ui/macros/nonterminal-matching.stderr
src/test/ui/parser/issues/issue-24780.rs
src/test/ui/parser/issues/issue-24780.stderr
src/test/ui/parser/issues/issue-58856-1.rs
src/test/ui/parser/issues/issue-58856-1.stderr
src/test/ui/parser/issues/issue-84148-1.stderr
src/test/ui/parser/issues/issue-84148-2.stderr
src/test/ui/parser/issues/issue-87635.rs
src/test/ui/parser/issues/issue-87635.stderr
src/test/ui/parser/missing_right_paren.stderr
src/test/ui/proc-macro/allowed-attr-stmt-expr.stdout
src/test/ui/proc-macro/attr-complex-fn.stdout
src/test/ui/proc-macro/attr-stmt-expr.stdout
src/test/ui/proc-macro/attribute-after-derive.stdout
src/test/ui/proc-macro/auxiliary/attr-args.rs
src/test/ui/proc-macro/auxiliary/attr-on-trait.rs
src/test/ui/proc-macro/cfg-eval-inner.stdout
src/test/ui/proc-macro/derive-expand-order.stdout
src/test/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout
src/test/ui/proc-macro/inner-attrs.stdout
src/test/ui/proc-macro/input-interpolated.stdout
src/test/ui/proc-macro/issue-75734-pp-paren.stdout
src/test/ui/proc-macro/issue-75930-derive-cfg.stdout
src/test/ui/proc-macro/issue-81007-item-attrs.stdout
src/test/ui/proc-macro/nested-macro-rules.stdout
src/test/ui/proc-macro/nonterminal-token-hygiene.stdout
src/test/ui/proc-macro/trailing-plus.stdout
src/test/ui/proc-macro/weird-braces.stdout
src/test/ui/repr/issue-83921-pretty.pretty.stdout
src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout
src/test/ui/rfc-2565-param-attrs/auxiliary/param-attrs.rs
src/test/ui/suggestions/count2len.rs [new file with mode: 0644]
src/test/ui/suggestions/count2len.stderr [new file with mode: 0644]
src/test/ui/suggestions/suggest-semicolon-for-fn-in-extern-block.fixed [new file with mode: 0644]
src/test/ui/suggestions/suggest-semicolon-for-fn-in-extern-block.rs [new file with mode: 0644]
src/test/ui/suggestions/suggest-semicolon-for-fn-in-extern-block.stderr [new file with mode: 0644]
src/test/ui/traits/issue-85360-eval-obligation-ice.rs [new file with mode: 0644]
src/test/ui/traits/issue-85360-eval-obligation-ice.stderr [new file with mode: 0644]
src/test/ui/typeck/issue-91267.rs [new file with mode: 0644]
src/test/ui/typeck/issue-91267.stderr [new file with mode: 0644]
src/test/ui/typeck/issue-91334.rs [new file with mode: 0644]
src/test/ui/typeck/issue-91334.stderr [new file with mode: 0644]
src/test/ui/typeck/issue-91450-inner-ty-error.rs [new file with mode: 0644]
src/test/ui/typeck/issue-91450-inner-ty-error.stderr [new file with mode: 0644]
src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md [deleted file]
src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.yml [new file with mode: 0644]
src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md [deleted file]
src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml [new file with mode: 0644]
src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md [deleted file]
src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.yml [new file with mode: 0644]
src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md [deleted file]
src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.yml [new file with mode: 0644]
src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md [deleted file]
src/tools/clippy/.github/ISSUE_TEMPLATE/ice.yml [new file with mode: 0644]
src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md [deleted file]
src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml [new file with mode: 0644]
src/tools/clippy/.github/workflows/clippy_dev.yml
src/tools/clippy/CHANGELOG.md
src/tools/clippy/Cargo.toml
src/tools/clippy/clippy_dev/Cargo.toml
src/tools/clippy/clippy_dev/src/fmt.rs
src/tools/clippy/clippy_dev/src/lib.rs
src/tools/clippy/clippy_dev/src/lint.rs [new file with mode: 0644]
src/tools/clippy/clippy_dev/src/main.rs
src/tools/clippy/clippy_dev/src/new_lint.rs
src/tools/clippy/clippy_dev/src/update_lints.rs
src/tools/clippy/clippy_lints/Cargo.toml
src/tools/clippy/clippy_lints/src/absurd_extreme_comparisons.rs
src/tools/clippy/clippy_lints/src/approx_const.rs
src/tools/clippy/clippy_lints/src/arithmetic.rs
src/tools/clippy/clippy_lints/src/as_conversions.rs
src/tools/clippy/clippy_lints/src/asm_syntax.rs
src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
src/tools/clippy/clippy_lints/src/assign_ops.rs
src/tools/clippy/clippy_lints/src/async_yields_async.rs
src/tools/clippy/clippy_lints/src/attrs.rs
src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
src/tools/clippy/clippy_lints/src/bit_mask.rs
src/tools/clippy/clippy_lints/src/blacklisted_name.rs
src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
src/tools/clippy/clippy_lints/src/booleans.rs
src/tools/clippy/clippy_lints/src/bytecount.rs
src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs
src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
src/tools/clippy/clippy_lints/src/casts/mod.rs
src/tools/clippy/clippy_lints/src/checked_conversions.rs
src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
src/tools/clippy/clippy_lints/src/collapsible_if.rs
src/tools/clippy/clippy_lints/src/collapsible_match.rs
src/tools/clippy/clippy_lints/src/comparison_chain.rs
src/tools/clippy/clippy_lints/src/copies.rs
src/tools/clippy/clippy_lints/src/copy_iterator.rs
src/tools/clippy/clippy_lints/src/create_dir.rs
src/tools/clippy/clippy_lints/src/dbg_macro.rs
src/tools/clippy/clippy_lints/src/default.rs
src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
src/tools/clippy/clippy_lints/src/deprecated_lints.rs
src/tools/clippy/clippy_lints/src/dereference.rs
src/tools/clippy/clippy_lints/src/derivable_impls.rs
src/tools/clippy/clippy_lints/src/derive.rs
src/tools/clippy/clippy_lints/src/disallowed_method.rs [deleted file]
src/tools/clippy/clippy_lints/src/disallowed_methods.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
src/tools/clippy/clippy_lints/src/disallowed_type.rs [deleted file]
src/tools/clippy/clippy_lints/src/disallowed_types.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/doc.rs
src/tools/clippy/clippy_lints/src/double_comparison.rs
src/tools/clippy/clippy_lints/src/double_parens.rs
src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
src/tools/clippy/clippy_lints/src/duration_subsec.rs
src/tools/clippy/clippy_lints/src/else_if_without_else.rs
src/tools/clippy/clippy_lints/src/empty_enum.rs
src/tools/clippy/clippy_lints/src/entry.rs
src/tools/clippy/clippy_lints/src/enum_clike.rs
src/tools/clippy/clippy_lints/src/enum_variants.rs
src/tools/clippy/clippy_lints/src/eq_op.rs
src/tools/clippy/clippy_lints/src/equatable_if_let.rs
src/tools/clippy/clippy_lints/src/erasing_op.rs
src/tools/clippy/clippy_lints/src/escape.rs
src/tools/clippy/clippy_lints/src/eta_reduction.rs
src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
src/tools/clippy/clippy_lints/src/excessive_bools.rs
src/tools/clippy/clippy_lints/src/exhaustive_items.rs
src/tools/clippy/clippy_lints/src/exit.rs
src/tools/clippy/clippy_lints/src/explicit_write.rs
src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
src/tools/clippy/clippy_lints/src/feature_name.rs
src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs
src/tools/clippy/clippy_lints/src/float_literal.rs
src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
src/tools/clippy/clippy_lints/src/format.rs
src/tools/clippy/clippy_lints/src/format_args.rs
src/tools/clippy/clippy_lints/src/formatting.rs
src/tools/clippy/clippy_lints/src/from_over_into.rs
src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
src/tools/clippy/clippy_lints/src/functions/mod.rs
src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
src/tools/clippy/clippy_lints/src/future_not_send.rs
src/tools/clippy/clippy_lints/src/get_last_with_len.rs
src/tools/clippy/clippy_lints/src/identity_op.rs
src/tools/clippy/clippy_lints/src/if_let_mutex.rs
src/tools/clippy/clippy_lints/src/if_not_else.rs
src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
src/tools/clippy/clippy_lints/src/implicit_hasher.rs
src/tools/clippy/clippy_lints/src/implicit_return.rs
src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
src/tools/clippy/clippy_lints/src/index_refutable_slice.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/indexing_slicing.rs
src/tools/clippy/clippy_lints/src/infinite_iter.rs
src/tools/clippy/clippy_lints/src/inherent_impl.rs
src/tools/clippy/clippy_lints/src/inherent_to_string.rs
src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
src/tools/clippy/clippy_lints/src/int_plus_one.rs
src/tools/clippy/clippy_lints/src/integer_division.rs
src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs
src/tools/clippy/clippy_lints/src/items_after_statements.rs
src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
src/tools/clippy/clippy_lints/src/large_const_arrays.rs
src/tools/clippy/clippy_lints/src/large_enum_variant.rs
src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
src/tools/clippy/clippy_lints/src/len_zero.rs
src/tools/clippy/clippy_lints/src/let_if_seq.rs
src/tools/clippy/clippy_lints/src/let_underscore.rs
src/tools/clippy/clippy_lints/src/lib.register_all.rs
src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
src/tools/clippy/clippy_lints/src/lib.register_internal.rs
src/tools/clippy/clippy_lints/src/lib.register_lints.rs
src/tools/clippy/clippy_lints/src/lib.register_nursery.rs
src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs
src/tools/clippy/clippy_lints/src/lib.register_style.rs
src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/lifetimes.rs
src/tools/clippy/clippy_lints/src/literal_representation.rs
src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs
src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
src/tools/clippy/clippy_lints/src/loops/mod.rs
src/tools/clippy/clippy_lints/src/loops/needless_collect.rs
src/tools/clippy/clippy_lints/src/loops/utils.rs
src/tools/clippy/clippy_lints/src/macro_use.rs
src/tools/clippy/clippy_lints/src/main_recursion.rs
src/tools/clippy/clippy_lints/src/manual_assert.rs
src/tools/clippy/clippy_lints/src/manual_async_fn.rs
src/tools/clippy/clippy_lints/src/manual_map.rs
src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
src/tools/clippy/clippy_lints/src/manual_ok_or.rs
src/tools/clippy/clippy_lints/src/manual_strip.rs
src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
src/tools/clippy/clippy_lints/src/map_clone.rs
src/tools/clippy/clippy_lints/src/map_err_ignore.rs
src/tools/clippy/clippy_lints/src/map_unit_fn.rs
src/tools/clippy/clippy_lints/src/match_on_vec_items.rs
src/tools/clippy/clippy_lints/src/match_result_ok.rs
src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs
src/tools/clippy/clippy_lints/src/matches.rs
src/tools/clippy/clippy_lints/src/mem_forget.rs
src/tools/clippy/clippy_lints/src/mem_replace.rs
src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs
src/tools/clippy/clippy_lints/src/methods/manual_split_once.rs [deleted file]
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs
src/tools/clippy/clippy_lints/src/methods/str_splitn.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
src/tools/clippy/clippy_lints/src/methods/utils.rs
src/tools/clippy/clippy_lints/src/minmax.rs
src/tools/clippy/clippy_lints/src/misc.rs
src/tools/clippy/clippy_lints/src/misc_early/mod.rs
src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
src/tools/clippy/clippy_lints/src/missing_doc.rs
src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
src/tools/clippy/clippy_lints/src/missing_inline.rs
src/tools/clippy/clippy_lints/src/module_style.rs
src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs
src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs
src/tools/clippy/clippy_lints/src/mut_key.rs
src/tools/clippy/clippy_lints/src/mut_mut.rs
src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs
src/tools/clippy/clippy_lints/src/mut_reference.rs
src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs
src/tools/clippy/clippy_lints/src/mutex_atomic.rs
src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs
src/tools/clippy/clippy_lints/src/needless_bool.rs
src/tools/clippy/clippy_lints/src/needless_borrow.rs [deleted file]
src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
src/tools/clippy/clippy_lints/src/needless_continue.rs
src/tools/clippy/clippy_lints/src/needless_for_each.rs
src/tools/clippy/clippy_lints/src/needless_late_init.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/needless_option_as_deref.rs
src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
src/tools/clippy/clippy_lints/src/needless_question_mark.rs
src/tools/clippy/clippy_lints/src/needless_update.rs
src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs
src/tools/clippy/clippy_lints/src/neg_multiply.rs
src/tools/clippy/clippy_lints/src/new_without_default.rs
src/tools/clippy/clippy_lints/src/no_effect.rs
src/tools/clippy/clippy_lints/src/non_copy_const.rs
src/tools/clippy/clippy_lints/src/non_expressive_names.rs
src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
src/tools/clippy/clippy_lints/src/octal_escapes.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/open_options.rs
src/tools/clippy/clippy_lints/src/option_env_unwrap.rs
src/tools/clippy/clippy_lints/src/option_if_let_else.rs
src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs
src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs
src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
src/tools/clippy/clippy_lints/src/precedence.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/ptr_eq.rs
src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
src/tools/clippy/clippy_lints/src/question_mark.rs
src/tools/clippy/clippy_lints/src/ranges.rs
src/tools/clippy/clippy_lints/src/redundant_clone.rs
src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
src/tools/clippy/clippy_lints/src/redundant_else.rs
src/tools/clippy/clippy_lints/src/redundant_field_names.rs
src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
src/tools/clippy/clippy_lints/src/redundant_slicing.rs
src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
src/tools/clippy/clippy_lints/src/ref_option_ref.rs
src/tools/clippy/clippy_lints/src/reference.rs
src/tools/clippy/clippy_lints/src/regex.rs
src/tools/clippy/clippy_lints/src/repeat_once.rs
src/tools/clippy/clippy_lints/src/returns.rs
src/tools/clippy/clippy_lints/src/same_name_method.rs
src/tools/clippy/clippy_lints/src/self_assignment.rs
src/tools/clippy/clippy_lints/src/self_named_constructors.rs
src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
src/tools/clippy/clippy_lints/src/serde_api.rs
src/tools/clippy/clippy_lints/src/shadow.rs
src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs
src/tools/clippy/clippy_lints/src/strings.rs
src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs
src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
src/tools/clippy/clippy_lints/src/swap.rs
src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
src/tools/clippy/clippy_lints/src/temporary_assignment.rs
src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
src/tools/clippy/clippy_lints/src/to_string_in_display.rs
src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
src/tools/clippy/clippy_lints/src/trait_bounds.rs
src/tools/clippy/clippy_lints/src/transmute/mod.rs
src/tools/clippy/clippy_lints/src/transmuting_null.rs
src/tools/clippy/clippy_lints/src/try_err.rs
src/tools/clippy/clippy_lints/src/types/mod.rs
src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs
src/tools/clippy/clippy_lints/src/unicode.rs
src/tools/clippy/clippy_lints/src/uninit_vec.rs
src/tools/clippy/clippy_lints/src/unit_hash.rs
src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
src/tools/clippy/clippy_lints/src/unit_types/mod.rs
src/tools/clippy/clippy_lints/src/unnamed_address.rs
src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
src/tools/clippy/clippy_lints/src/unused_async.rs
src/tools/clippy/clippy_lints/src/unused_io_amount.rs
src/tools/clippy/clippy_lints/src/unused_self.rs
src/tools/clippy/clippy_lints/src/unused_unit.rs
src/tools/clippy/clippy_lints/src/unwrap.rs
src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
src/tools/clippy/clippy_lints/src/use_self.rs
src/tools/clippy/clippy_lints/src/useless_conversion.rs
src/tools/clippy/clippy_lints/src/utils/author.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
src/tools/clippy/clippy_lints/src/vec.rs
src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs
src/tools/clippy/clippy_lints/src/verbose_file_reads.rs
src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs
src/tools/clippy/clippy_lints/src/wildcard_imports.rs
src/tools/clippy/clippy_lints/src/write.rs
src/tools/clippy/clippy_lints/src/zero_div_zero.rs
src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
src/tools/clippy/clippy_utils/Cargo.toml
src/tools/clippy/clippy_utils/src/ast_utils.rs
src/tools/clippy/clippy_utils/src/attrs.rs
src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/msrvs.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/ptr.rs
src/tools/clippy/clippy_utils/src/source.rs
src/tools/clippy/clippy_utils/src/sugg.rs
src/tools/clippy/clippy_utils/src/ty.rs
src/tools/clippy/clippy_utils/src/usage.rs
src/tools/clippy/clippy_utils/src/visitors.rs
src/tools/clippy/doc/adding_lints.md
src/tools/clippy/doc/changelog_update.md
src/tools/clippy/doc/common_tools_writing_lints.md
src/tools/clippy/lintcheck/src/main.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/src/driver.rs
src/tools/clippy/tests/compile-test.rs
src/tools/clippy/tests/fmt.rs
src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs [new file with mode: 0644]
src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed
src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs
src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr
src/tools/clippy/tests/ui-internal/custom_ice_message.rs
src/tools/clippy/tests/ui-internal/default_lint.rs
src/tools/clippy/tests/ui-internal/default_lint.stderr
src/tools/clippy/tests/ui-internal/if_chain_style.rs
src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed
src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs
src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr
src/tools/clippy/tests/ui-internal/invalid_paths.rs
src/tools/clippy/tests/ui-internal/invalid_paths.stderr
src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs
src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr
src/tools/clippy/tests/ui-internal/match_type_on_diag_item.rs
src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr
src/tools/clippy/tests/ui-internal/outer_expn_data.fixed
src/tools/clippy/tests/ui-internal/outer_expn_data.rs
src/tools/clippy/tests/ui-internal/outer_expn_data.stderr
src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed
src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs
src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr
src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/clippy.toml [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs
src/tools/clippy/tests/ui-toml/toml_disallowed_method/clippy.toml [deleted file]
src/tools/clippy/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs [deleted file]
src/tools/clippy/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr [deleted file]
src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/toml_disallowed_type/clippy.toml [deleted file]
src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs [deleted file]
src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr [deleted file]
src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
src/tools/clippy/tests/ui/author.stdout
src/tools/clippy/tests/ui/author/blocks.rs
src/tools/clippy/tests/ui/author/blocks.stdout
src/tools/clippy/tests/ui/author/call.stdout
src/tools/clippy/tests/ui/author/for_loop.rs [deleted file]
src/tools/clippy/tests/ui/author/for_loop.stdout [deleted file]
src/tools/clippy/tests/ui/author/if.rs
src/tools/clippy/tests/ui/author/if.stdout
src/tools/clippy/tests/ui/author/issue_3849.stdout
src/tools/clippy/tests/ui/author/loop.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/author/loop.stdout [new file with mode: 0644]
src/tools/clippy/tests/ui/author/matches.stdout
src/tools/clippy/tests/ui/author/repeat.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/author/repeat.stdout [new file with mode: 0644]
src/tools/clippy/tests/ui/author/struct.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/author/struct.stdout [new file with mode: 0644]
src/tools/clippy/tests/ui/cast_lossless_bool.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/cast_lossless_bool.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/cast_lossless_bool.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/crashes/auxiliary/ice-7934-aux.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/crashes/ice-7934.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/doc/doc-fixable.stderr
src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
src/tools/clippy/tests/ui/explicit_counter_loop.rs
src/tools/clippy/tests/ui/explicit_counter_loop.stderr
src/tools/clippy/tests/ui/floating_point_abs.fixed
src/tools/clippy/tests/ui/floating_point_abs.rs
src/tools/clippy/tests/ui/floating_point_abs.stderr
src/tools/clippy/tests/ui/floating_point_mul_add.fixed
src/tools/clippy/tests/ui/floating_point_mul_add.rs
src/tools/clippy/tests/ui/floating_point_mul_add.stderr
src/tools/clippy/tests/ui/floating_point_rad.fixed
src/tools/clippy/tests/ui/floating_point_rad.rs
src/tools/clippy/tests/ui/floating_point_rad.stderr
src/tools/clippy/tests/ui/if_then_some_else_none.rs
src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/iter_cloned_collect.fixed
src/tools/clippy/tests/ui/iter_cloned_collect.rs
src/tools/clippy/tests/ui/iter_cloned_collect.stderr
src/tools/clippy/tests/ui/let_if_seq.rs
src/tools/clippy/tests/ui/let_if_seq.stderr
src/tools/clippy/tests/ui/let_underscore_lock.rs
src/tools/clippy/tests/ui/let_underscore_lock.stderr
src/tools/clippy/tests/ui/manual_assert.edition2018.fixed
src/tools/clippy/tests/ui/manual_assert.edition2018.stderr
src/tools/clippy/tests/ui/manual_assert.edition2021.fixed
src/tools/clippy/tests/ui/manual_assert.edition2021.stderr
src/tools/clippy/tests/ui/manual_assert.fixed
src/tools/clippy/tests/ui/manual_assert.rs
src/tools/clippy/tests/ui/manual_map_option_2.fixed
src/tools/clippy/tests/ui/manual_map_option_2.rs
src/tools/clippy/tests/ui/manual_map_option_2.stderr
src/tools/clippy/tests/ui/manual_split_once.fixed
src/tools/clippy/tests/ui/manual_split_once.rs
src/tools/clippy/tests/ui/manual_split_once.stderr
src/tools/clippy/tests/ui/match_overlapping_arm.rs
src/tools/clippy/tests/ui/match_overlapping_arm.stderr
src/tools/clippy/tests/ui/min_max.rs
src/tools/clippy/tests/ui/min_max.stderr
src/tools/clippy/tests/ui/min_rust_version_attr.rs
src/tools/clippy/tests/ui/min_rust_version_attr.stderr
src/tools/clippy/tests/ui/needless_borrow.fixed
src/tools/clippy/tests/ui/needless_borrow.rs
src/tools/clippy/tests/ui/needless_borrow.stderr
src/tools/clippy/tests/ui/needless_collect_indirect.rs
src/tools/clippy/tests/ui/needless_late_init.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_late_init.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_late_init_fixable.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_late_init_fixable.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_late_init_fixable.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_question_mark.stderr
src/tools/clippy/tests/ui/needless_return.fixed
src/tools/clippy/tests/ui/needless_return.rs
src/tools/clippy/tests/ui/needless_return.stderr
src/tools/clippy/tests/ui/needless_splitn.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_splitn.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_splitn.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/no_effect.rs
src/tools/clippy/tests/ui/no_effect.stderr
src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs
src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr
src/tools/clippy/tests/ui/octal_escapes.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/octal_escapes.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/option_env_unwrap.rs
src/tools/clippy/tests/ui/option_env_unwrap.stderr
src/tools/clippy/tests/ui/option_filter_map.fixed
src/tools/clippy/tests/ui/option_filter_map.rs
src/tools/clippy/tests/ui/option_filter_map.stderr
src/tools/clippy/tests/ui/option_if_let_else.fixed
src/tools/clippy/tests/ui/option_if_let_else.rs
src/tools/clippy/tests/ui/option_if_let_else.stderr
src/tools/clippy/tests/ui/option_map_or_none.fixed
src/tools/clippy/tests/ui/option_map_or_none.rs
src/tools/clippy/tests/ui/option_map_or_none.stderr
src/tools/clippy/tests/ui/or_fun_call.fixed
src/tools/clippy/tests/ui/or_fun_call.rs
src/tools/clippy/tests/ui/or_fun_call.stderr
src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs
src/tools/clippy/tests/ui/redundant_closure_call_late.rs
src/tools/clippy/tests/ui/redundant_closure_call_late.stderr
src/tools/clippy/tests/ui/redundant_else.rs
src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
src/tools/clippy/tests/ui/rename.fixed
src/tools/clippy/tests/ui/rename.rs
src/tools/clippy/tests/ui/rename.stderr
src/tools/clippy/tests/ui/same_name_method.stderr
src/tools/clippy/tests/ui/search_is_some.rs
src/tools/clippy/tests/ui/search_is_some.stderr
src/tools/clippy/tests/ui/search_is_some_fixable.fixed [deleted file]
src/tools/clippy/tests/ui/search_is_some_fixable.rs [deleted file]
src/tools/clippy/tests/ui/search_is_some_fixable.stderr [deleted file]
src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/search_is_some_fixable_none.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/search_is_some_fixable_some.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs
src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr
src/tools/clippy/tests/ui/shadow.rs
src/tools/clippy/tests/ui/shadow.stderr
src/tools/clippy/tests/ui/single_char_pattern.fixed
src/tools/clippy/tests/ui/single_char_pattern.rs
src/tools/clippy/tests/ui/single_char_pattern.stderr
src/tools/clippy/tests/ui/strlen_on_c_strings.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/strlen_on_c_strings.rs
src/tools/clippy/tests/ui/strlen_on_c_strings.stderr
src/tools/clippy/tests/ui/suspicious_splitn.rs
src/tools/clippy/tests/ui/suspicious_splitn.stderr
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
src/tools/clippy/tests/ui/unicode.rs
src/tools/clippy/tests/ui/unicode.stderr
src/tools/clippy/triagebot.toml
src/tools/clippy/util/gh-pages/index.html
src/tools/compiletest/src/runtest.rs
src/tools/miri
src/tools/rust-analyzer
src/tools/rustbook/Cargo.toml
src/tools/rustdoc-gui/tester.js

index 2233162be3b6b464749f9d474f7d189064dbcd10..92e13881f8de47859301fb0dbd9bf461bc177819 100644 (file)
@@ -574,7 +574,7 @@ dependencies = [
 
 [[package]]
 name = "clippy"
-version = "0.1.58"
+version = "0.1.59"
 dependencies = [
  "cargo_metadata 0.14.0",
  "clippy_lints",
@@ -584,6 +584,7 @@ dependencies = [
  "filetime",
  "if_chain",
  "itertools 0.10.1",
+ "parking_lot",
  "quote",
  "regex",
  "rustc-workspace-hack",
@@ -600,6 +601,7 @@ name = "clippy_dev"
 version = "0.0.1"
 dependencies = [
  "bytecount",
+ "cargo_metadata 0.14.0",
  "clap",
  "indoc",
  "itertools 0.10.1",
@@ -611,13 +613,13 @@ dependencies = [
 
 [[package]]
 name = "clippy_lints"
-version = "0.1.58"
+version = "0.1.59"
 dependencies = [
  "cargo_metadata 0.14.0",
  "clippy_utils",
  "if_chain",
  "itertools 0.10.1",
- "pulldown-cmark 0.8.0",
+ "pulldown-cmark",
  "quine-mc_cluskey",
  "regex-syntax",
  "rustc-semver",
@@ -632,7 +634,7 @@ dependencies = [
 
 [[package]]
 name = "clippy_utils"
-version = "0.1.58"
+version = "0.1.59"
 dependencies = [
  "if_chain",
  "rustc-semver",
@@ -1713,9 +1715,12 @@ dependencies = [
 
 [[package]]
 name = "instant"
-version = "0.1.6"
+version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b141fdc7836c525d4d594027d318c84161ca17aaf8113ab1f81ab93ae897485"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if 1.0.0",
+]
 
 [[package]]
 name = "itertools"
@@ -1992,9 +1997,9 @@ version = "0.1.0"
 
 [[package]]
 name = "lock_api"
-version = "0.4.1"
+version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c"
+checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
 dependencies = [
  "scopeguard",
 ]
@@ -2124,9 +2129,9 @@ dependencies = [
 
 [[package]]
 name = "mdbook"
-version = "0.4.12"
+version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0651782b4cc514c3f98c0acf9b5af1101a735bbe1ac6852bb1a90cb91bdf0ed4"
+checksum = "f6e77253c46a90eb7e96b2807201dab941a4db5ea05eca5aaaf7027395f352b3"
 dependencies = [
  "ammonia",
  "anyhow",
@@ -2138,8 +2143,8 @@ dependencies = [
  "lazy_static",
  "log",
  "memchr",
- "open",
- "pulldown-cmark 0.7.2",
+ "opener",
+ "pulldown-cmark",
  "regex",
  "serde",
  "serde_derive",
@@ -2147,6 +2152,7 @@ dependencies = [
  "shlex",
  "tempfile",
  "toml",
+ "topological-sort",
 ]
 
 [[package]]
@@ -2374,15 +2380,6 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
 
-[[package]]
-name = "open"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c283bf0114efea9e42f1a60edea9859e8c47528eae09d01df4b29c1e489cc48"
-dependencies = [
- "winapi",
-]
-
 [[package]]
 name = "opener"
 version = "0.5.0"
@@ -2511,9 +2508,9 @@ dependencies = [
 
 [[package]]
 name = "parking_lot"
-version = "0.11.1"
+version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
 dependencies = [
  "instant",
  "lock_api",
@@ -2522,9 +2519,9 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.8.3"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
+checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
 dependencies = [
  "cfg-if 1.0.0",
  "instant",
@@ -2794,18 +2791,6 @@ dependencies = [
  "cc",
 ]
 
-[[package]]
-name = "pulldown-cmark"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca36dea94d187597e104a5c8e4b07576a8a45aa5db48a65e12940d3eb7461f55"
-dependencies = [
- "bitflags",
- "getopts",
- "memchr",
- "unicase",
-]
-
 [[package]]
 name = "pulldown-cmark"
 version = "0.8.0"
@@ -2813,6 +2798,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8"
 dependencies = [
  "bitflags",
+ "getopts",
  "memchr",
  "unicase",
 ]
@@ -4616,7 +4602,7 @@ dependencies = [
  "expect-test",
  "itertools 0.9.0",
  "minifier",
- "pulldown-cmark 0.8.0",
+ "pulldown-cmark",
  "rayon",
  "regex",
  "rustdoc-json-types",
@@ -5393,6 +5379,12 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "topological-sort"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa7c7f42dea4b1b99439786f5633aeb9c14c1b53f75e282803c2ec2ad545873c"
+
 [[package]]
 name = "tower-service"
 version = "0.3.1"
index dce856df9c66abeb4087f173ce29b64ea929f0e9..5ec71cddf7de6bfc86be59362e38af97dcc16499 100644 (file)
@@ -35,4 +35,14 @@ pub fn word_nbsp<S: Into<Cow<'static, str>>>(&mut self, w: S) {
         self.word(w);
         self.nbsp()
     }
+
+    // Synthesizes a comment that was not textually present in the original
+    // source file.
+    pub fn synth_comment(&mut self, text: impl Into<Cow<'static, str>>) {
+        self.word("/*");
+        self.space();
+        self.word(text);
+        self.space();
+        self.word("*/")
+    }
 }
index 0dc1f093947e3b092ea8645d86f92a4c7a875c86..921ac785e815e72c00313638a793bd1942e6b02c 100644 (file)
@@ -263,14 +263,17 @@ fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], op: F)
         self.strsep(",", false, b, elts, op)
     }
 
-    fn maybe_print_comment(&mut self, pos: BytePos) {
+    fn maybe_print_comment(&mut self, pos: BytePos) -> bool {
+        let mut has_comment = false;
         while let Some(ref cmnt) = self.next_comment() {
             if cmnt.pos < pos {
+                has_comment = true;
                 self.print_comment(cmnt);
             } else {
                 break;
             }
         }
+        has_comment
     }
 
     fn print_comment(&mut self, cmnt: &Comment) {
@@ -346,6 +349,25 @@ fn next_comment(&mut self) -> Option<Comment> {
         self.comments().as_mut().and_then(|c| c.next())
     }
 
+    fn maybe_print_trailing_comment(&mut self, span: rustc_span::Span, next_pos: Option<BytePos>) {
+        if let Some(cmnts) = self.comments() {
+            if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) {
+                self.print_comment(&cmnt);
+            }
+        }
+    }
+
+    fn print_remaining_comments(&mut self) {
+        // If there aren't any remaining comments, then we need to manually
+        // make sure there is a line break at the end.
+        if self.next_comment().is_none() {
+            self.hardbreak();
+        }
+        while let Some(ref cmnt) = self.next_comment() {
+            self.print_comment(cmnt)
+        }
+    }
+
     fn print_literal(&mut self, lit: &ast::Lit) {
         self.maybe_print_comment(lit.span.lo());
         self.word(lit.token.to_string())
@@ -570,7 +592,10 @@ fn print_mac_common(
         self.print_tts(tts, convert_dollar_crate);
         self.end();
         match delim {
-            DelimToken::Brace => self.bclose(span),
+            DelimToken::Brace => {
+                let empty = tts.is_empty();
+                self.bclose(span, empty);
+            }
             _ => {
                 let token_str = self.token_kind_to_string(&token::CloseDelim(delim));
                 self.word(token_str)
@@ -642,17 +667,20 @@ fn bopen(&mut self) {
         self.end(); // Close the head-box.
     }
 
-    fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
-        self.maybe_print_comment(span.hi());
-        self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize));
+    fn bclose_maybe_open(&mut self, span: rustc_span::Span, empty: bool, close_box: bool) {
+        let has_comment = self.maybe_print_comment(span.hi());
+        if !empty || has_comment {
+            self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize));
+        }
         self.word("}");
         if close_box {
             self.end(); // Close the outer-box.
         }
     }
 
-    fn bclose(&mut self, span: rustc_span::Span) {
-        self.bclose_maybe_open(span, true)
+    fn bclose(&mut self, span: rustc_span::Span, empty: bool) {
+        let close_box = true;
+        self.bclose_maybe_open(span, empty, close_box)
     }
 
     fn break_offset_if_not_bol(&mut self, n: usize, off: isize) {
@@ -884,16 +912,6 @@ pub fn new() -> State<'a> {
         State { s: pp::mk_printer(), comments: None, ann: &NoAnn }
     }
 
-    // Synthesizes a comment that was not textually present in the original source
-    // file.
-    pub fn synth_comment(&mut self, text: String) {
-        self.s.word("/*");
-        self.s.space();
-        self.s.word(text);
-        self.s.space();
-        self.s.word("*/")
-    }
-
     crate fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
     where
         F: FnMut(&mut State<'_>, &T),
@@ -1196,7 +1214,8 @@ fn print_associated_type(
                         for item in items {
                             self.print_item(item);
                         }
-                        self.bclose(item.span);
+                        let empty = item.attrs.is_empty() && items.is_empty();
+                        self.bclose(item.span, empty);
                     }
                     ModKind::Unloaded => {
                         self.s.word(";");
@@ -1216,7 +1235,8 @@ fn print_associated_type(
                 }
                 self.bopen();
                 self.print_foreign_mod(nmod, &item.attrs);
-                self.bclose(item.span);
+                let empty = item.attrs.is_empty() && nmod.items.is_empty();
+                self.bclose(item.span, empty);
             }
             ast::ItemKind::GlobalAsm(ref asm) => {
                 self.head(visibility_qualified(&item.vis, "global_asm!"));
@@ -1291,7 +1311,8 @@ fn print_associated_type(
                 for impl_item in items {
                     self.print_assoc_item(impl_item);
                 }
-                self.bclose(item.span);
+                let empty = item.attrs.is_empty() && items.is_empty();
+                self.bclose(item.span, empty);
             }
             ast::ItemKind::Trait(box ast::Trait {
                 is_auto,
@@ -1326,7 +1347,8 @@ fn print_associated_type(
                 for trait_item in items {
                     self.print_assoc_item(trait_item);
                 }
-                self.bclose(item.span);
+                let empty = item.attrs.is_empty() && items.is_empty();
+                self.bclose(item.span, empty);
             }
             ast::ItemKind::TraitAlias(ref generics, ref bounds) => {
                 self.head("");
@@ -1410,7 +1432,8 @@ fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) {
             self.end();
             self.maybe_print_trailing_comment(v.span, None);
         }
-        self.bclose(span)
+        let empty = variants.is_empty();
+        self.bclose(span, empty)
     }
 
     crate fn print_visibility(&mut self, vis: &ast::Visibility) {
@@ -1441,20 +1464,24 @@ fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) {
     crate fn print_record_struct_body(&mut self, fields: &[ast::FieldDef], span: rustc_span::Span) {
         self.nbsp();
         self.bopen();
-        self.hardbreak_if_not_bol();
 
-        for field in fields {
+        let empty = fields.is_empty();
+        if !empty {
             self.hardbreak_if_not_bol();
-            self.maybe_print_comment(field.span.lo());
-            self.print_outer_attributes(&field.attrs);
-            self.print_visibility(&field.vis);
-            self.print_ident(field.ident.unwrap());
-            self.word_nbsp(":");
-            self.print_type(&field.ty);
-            self.s.word(",");
+
+            for field in fields {
+                self.hardbreak_if_not_bol();
+                self.maybe_print_comment(field.span.lo());
+                self.print_outer_attributes(&field.attrs);
+                self.print_visibility(&field.vis);
+                self.print_ident(field.ident.unwrap());
+                self.word_nbsp(":");
+                self.print_type(&field.ty);
+                self.s.word(",");
+            }
         }
 
-        self.bclose(span)
+        self.bclose(span, empty);
     }
 
     crate fn print_struct(
@@ -1633,7 +1660,8 @@ fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) {
             }
         }
 
-        self.bclose_maybe_open(blk.span, close_box);
+        let empty = attrs.is_empty() && blk.stmts.is_empty();
+        self.bclose_maybe_open(blk.span, empty, close_box);
         self.ann.post(self, AnnNode::Block(blk))
     }
 
@@ -2010,7 +2038,8 @@ fn print_expr_outer_attr_style(&mut self, expr: &ast::Expr, is_inline: bool) {
                 for arm in arms {
                     self.print_arm(arm);
                 }
-                self.bclose(expr.span);
+                let empty = attrs.is_empty() && arms.is_empty();
+                self.bclose(expr.span, empty);
             }
             ast::ExprKind::Closure(
                 capture_clause,
@@ -2048,7 +2077,6 @@ fn print_expr_outer_attr_style(&mut self, expr: &ast::Expr, is_inline: bool) {
             ast::ExprKind::Async(capture_clause, _, ref blk) => {
                 self.word_nbsp("async");
                 self.print_capture_clause(capture_clause);
-                self.s.space();
                 // cbox/ibox in analogy to the `ExprKind::Block` arm above
                 self.cbox(INDENT_UNIT);
                 self.ibox(0);
@@ -2900,29 +2928,6 @@ pub fn print_mutability(&mut self, mutbl: ast::Mutability, print_const: bool) {
         self.end();
     }
 
-    crate fn maybe_print_trailing_comment(
-        &mut self,
-        span: rustc_span::Span,
-        next_pos: Option<BytePos>,
-    ) {
-        if let Some(cmnts) = self.comments() {
-            if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) {
-                self.print_comment(&cmnt);
-            }
-        }
-    }
-
-    crate fn print_remaining_comments(&mut self) {
-        // If there aren't any remaining comments, then we need to manually
-        // make sure there is a line break at the end.
-        if self.next_comment().is_none() {
-            self.s.hardbreak();
-        }
-        while let Some(ref cmnt) = self.next_comment() {
-            self.print_comment(cmnt);
-        }
-    }
-
     crate fn print_fn_header_info(&mut self, header: ast::FnHeader) {
         self.print_constness(header.constness);
         self.print_asyncness(header.asyncness);
index 6a3b94a0d7018311a6979d04cd7aa4dca320a56b..7481b5db755e7a4a60f4957329c64afd101dcd1e 100644 (file)
@@ -577,6 +577,7 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => unimplemented!(),
             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) => unimplemented!(),
+            InlineAsmRegClass::Avr(_) => unimplemented!(),
             InlineAsmRegClass::Bpf(_) => unimplemented!(),
             InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => unimplemented!(),
             InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => unimplemented!(),
@@ -639,6 +640,7 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => {
             unimplemented!()
         }
+        InlineAsmRegClass::Avr(_) => unimplemented!(),
         InlineAsmRegClass::Bpf(_) => unimplemented!(),
         InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(),
         InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(),
@@ -747,6 +749,7 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option
         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => {
             unimplemented!()
         }
+        InlineAsmRegClass::Avr(_) => unimplemented!(),
         InlineAsmRegClass::Bpf(_) => unimplemented!(),
         InlineAsmRegClass::Hexagon(_) => unimplemented!(),
         InlineAsmRegClass::Mips(_) => unimplemented!(),
index 83c5cb6f1cf51ee28f975668cf55237301ca49da..90d3c0fb2f173acbbc335415334d0169b7fa940e 100644 (file)
@@ -319,6 +319,9 @@ fn codegen_inline_asm(
                         "~{vxrm}".to_string(),
                     ]);
                 }
+                InlineAsmArch::Avr => {
+                    constraints.push("~{sreg}".to_string());
+                }
                 InlineAsmArch::Nvptx64 => {}
                 InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => {}
                 InlineAsmArch::Hexagon => {}
@@ -669,6 +672,11 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>)
             InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r",
             InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r",
             InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w",
+            InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg) => "r",
+            InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_upper) => "d",
+            InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair) => "r",
+            InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) => "w",
+            InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => "e",
             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r",
             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f",
             InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
@@ -749,6 +757,14 @@ fn modifier_to_llvm(
         }
         InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => None,
         InlineAsmRegClass::Bpf(_) => None,
+        InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair)
+        | InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw)
+        | InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => match modifier {
+            Some('h') => Some('B'),
+            Some('l') => Some('A'),
+            _ => None,
+        },
+        InlineAsmRegClass::Avr(_) => None,
         InlineAsmRegClass::S390x(_) => None,
         InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
             bug!("LLVM backend does not support SPIR-V")
@@ -812,6 +828,11 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll
         InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(),
         InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => cx.type_i64(),
         InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => cx.type_i32(),
+        InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg) => cx.type_i8(),
+        InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_upper) => cx.type_i8(),
+        InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair) => cx.type_i16(),
+        InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) => cx.type_i16(),
+        InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => cx.type_i16(),
         InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => cx.type_i32(),
         InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
         InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
index cf084faade8cc6c69e8e5cabbe3fc13f7cc63b73..d46950ed90315c24f2d87d7c7fa6ecabbe7ac0ec 100644 (file)
@@ -7,6 +7,7 @@
 use rustc_index::vec::IndexVec;
 use rustc_macros::HashStable;
 use rustc_middle::mir;
+use rustc_middle::mir::interpret::{InterpError, InvalidProgramInfo};
 use rustc_middle::ty::layout::{self, LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::{
     self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable,
@@ -14,7 +15,7 @@
 use rustc_mir_dataflow::storage::AlwaysLiveLocals;
 use rustc_query_system::ich::StableHashingContext;
 use rustc_session::Limit;
-use rustc_span::{Pos, Span};
+use rustc_span::{Pos, Span, DUMMY_SP};
 use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout};
 
 use super::{
@@ -508,7 +509,7 @@ pub fn load_mir(
     pub(super) fn subst_from_current_frame_and_normalize_erasing_regions<T: TypeFoldable<'tcx>>(
         &self,
         value: T,
-    ) -> T {
+    ) -> Result<T, InterpError<'tcx>> {
         self.subst_from_frame_and_normalize_erasing_regions(self.frame(), value)
     }
 
@@ -518,8 +519,18 @@ pub(super) fn subst_from_frame_and_normalize_erasing_regions<T: TypeFoldable<'tc
         &self,
         frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>,
         value: T,
-    ) -> T {
-        frame.instance.subst_mir_and_normalize_erasing_regions(*self.tcx, self.param_env, value)
+    ) -> Result<T, InterpError<'tcx>> {
+        frame
+            .instance
+            .try_subst_mir_and_normalize_erasing_regions(*self.tcx, self.param_env, value)
+            .or_else(|e| {
+                self.tcx.sess.delay_span_bug(
+                    DUMMY_SP,
+                    format!("failed to normalize {}", e.get_type_for_failure()).as_str(),
+                );
+
+                Err(InterpError::InvalidProgram(InvalidProgramInfo::TooGeneric))
+            })
     }
 
     /// The `substs` are assumed to already be in our interpreter "universe" (param_env).
@@ -554,7 +565,7 @@ pub fn layout_of_local(
                 let layout = from_known_layout(self.tcx, self.param_env, layout, || {
                     let local_ty = frame.body.local_decls[local].ty;
                     let local_ty =
-                        self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty);
+                        self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty)?;
                     self.layout_of(local_ty)
                 })?;
                 if let Some(state) = frame.locals.get(local) {
@@ -702,7 +713,7 @@ pub fn push_stack_frame(
         for const_ in &body.required_consts {
             let span = const_.span;
             let const_ =
-                self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal);
+                self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal)?;
             self.mir_const_to_op(&const_, None).map_err(|err| {
                 // If there was an error, set the span of the current frame to this constant.
                 // Avoiding doing this when evaluation succeeds.
index de9e94ce2ac0c820137e5edcb9c3f68ce20f848e..e82ce73c814ff1a65f9aae47b180b1f61c1d326d 100644 (file)
@@ -512,7 +512,7 @@ pub fn eval_place_to_op(
             self.param_env,
             self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
                 place.ty(&self.frame().body.local_decls, *self.tcx).ty
-            ))?,
+            )?)?,
             op.layout,
         ));
         Ok(op)
@@ -534,7 +534,7 @@ pub fn eval_operand(
 
             Constant(ref constant) => {
                 let val =
-                    self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal);
+                    self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal)?;
                 // This can still fail:
                 // * During ConstProp, with `TooGeneric` or since the `requried_consts` were not all
                 //   checked yet.
index d7f2853fc86f508a26d70aa3b61580c97db0a2f6..4c95da896a2230a11d6793e3bf18e468d12a0173 100644 (file)
@@ -643,7 +643,7 @@ pub fn eval_place(
             self.param_env,
             self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
                 place.ty(&self.frame().body.local_decls, *self.tcx).ty
-            ))?,
+            )?)?,
             place_ty.layout,
         ));
         Ok(place_ty)
index 9299ae2b2b9f8f6708f9a1a1646e23818a96f260..992cef1cb6aa0cb8151138beeea64c094f4b4839 100644 (file)
@@ -276,7 +276,7 @@ pub fn eval_rvalue_into_place(
             }
 
             NullaryOp(null_op, ty) => {
-                let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty);
+                let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty)?;
                 let layout = self.layout_of(ty)?;
                 if layout.is_unsized() {
                     // FIXME: This should be a span_bug (#80742)
@@ -302,7 +302,8 @@ pub fn eval_rvalue_into_place(
 
             Cast(cast_kind, ref operand, cast_ty) => {
                 let src = self.eval_operand(operand, None)?;
-                let cast_ty = self.subst_from_current_frame_and_normalize_erasing_regions(cast_ty);
+                let cast_ty =
+                    self.subst_from_current_frame_and_normalize_erasing_regions(cast_ty)?;
                 self.cast(&src, cast_kind, cast_ty, &dest)?;
             }
 
index 274665ccd98364f7f4f90d060247840e6f4ca46e..4a02e0595376b7f1bd1bbe0718f689ade9e114d3 100644 (file)
@@ -801,7 +801,7 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
                 if let Some(trait_id) = tcx.trait_of_item(callee) {
                     trace!("attempting to call a trait method");
                     if !self.tcx.features().const_trait_impl {
-                        self.check_op(ops::FnCallNonConst);
+                        self.check_op(ops::FnCallNonConst(Some((callee, substs))));
                         return;
                     }
 
@@ -857,7 +857,7 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
                             }
 
                             if !nonconst_call_permission {
-                                self.check_op(ops::FnCallNonConst);
+                                self.check_op(ops::FnCallNonConst(None));
                                 return;
                             }
                         }
@@ -926,7 +926,7 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
                     }
 
                     if !nonconst_call_permission {
-                        self.check_op(ops::FnCallNonConst);
+                        self.check_op(ops::FnCallNonConst(None));
                         return;
                     }
                 }
index 6391c88600936f12796e25f4fcc514359a09c291..421c559474a97d642b1720367355187aa556d5cd 100644 (file)
@@ -1,12 +1,14 @@
 //! Concrete error types for all operations which may be invalid in a certain const context.
 
-use rustc_errors::{struct_span_err, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_middle::mir;
+use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
+use rustc_middle::{mir, ty::AssocKind};
 use rustc_session::parse::feature_err;
 use rustc_span::symbol::sym;
-use rustc_span::{Span, Symbol};
+use rustc_span::{symbol::Ident, Span, Symbol};
+use rustc_span::{BytePos, Pos};
 
 use super::ConstCx;
 
@@ -72,17 +74,71 @@ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<
 
 /// A function call where the callee is not marked as `const`.
 #[derive(Debug)]
-pub struct FnCallNonConst;
-impl NonConstOp for FnCallNonConst {
+pub struct FnCallNonConst<'tcx>(pub Option<(DefId, SubstsRef<'tcx>)>);
+impl<'a> NonConstOp for FnCallNonConst<'a> {
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
-        struct_span_err!(
+        let mut err = struct_span_err!(
             ccx.tcx.sess,
             span,
             E0015,
             "calls in {}s are limited to constant functions, \
              tuple structs and tuple variants",
             ccx.const_kind(),
-        )
+        );
+
+        if let FnCallNonConst(Some((callee, substs))) = *self {
+            if let Some(trait_def_id) = ccx.tcx.lang_items().eq_trait() {
+                if let Some(eq_item) = ccx.tcx.associated_items(trait_def_id).find_by_name_and_kind(
+                    ccx.tcx,
+                    Ident::with_dummy_span(sym::eq),
+                    AssocKind::Fn,
+                    trait_def_id,
+                ) {
+                    if callee == eq_item.def_id && substs.len() == 2 {
+                        match (substs[0].unpack(), substs[1].unpack()) {
+                            (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
+                                if self_ty == rhs_ty
+                                    && self_ty.is_ref()
+                                    && self_ty.peel_refs().is_primitive() =>
+                            {
+                                let mut num_refs = 0;
+                                let mut tmp_ty = self_ty;
+                                while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
+                                    num_refs += 1;
+                                    tmp_ty = inner_ty;
+                                }
+                                let deref = "*".repeat(num_refs);
+
+                                if let Ok(call_str) =
+                                    ccx.tcx.sess.source_map().span_to_snippet(span)
+                                {
+                                    if let Some(eq_idx) = call_str.find("==") {
+                                        if let Some(rhs_idx) = call_str[(eq_idx + 2)..]
+                                            .find(|c: char| !c.is_whitespace())
+                                        {
+                                            let rhs_pos = span.lo()
+                                                + BytePos::from_usize(eq_idx + 2 + rhs_idx);
+                                            let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
+                                            err.multipart_suggestion(
+                                                "consider dereferencing here",
+                                                vec![
+                                                    (span.shrink_to_lo(), deref.clone()),
+                                                    (rhs_span, deref),
+                                                ],
+                                                Applicability::MachineApplicable,
+                                            );
+                                        }
+                                    }
+                                }
+                            }
+                            _ => {}
+                        }
+                    }
+                }
+            }
+        }
+
+        err
     }
 }
 
index ede5757a479de273216921e6ec9e04ee8b8b8ce9..bf09b2f8eef37dfb530628aa665e24f466dda93e 100644 (file)
@@ -6,7 +6,7 @@
 /// function finds the range of elements that match the key. `data`
 /// must have been sorted as if by a call to `sort_by_key` for this to
 /// work.
-pub fn binary_search_slice<E, K>(data: &'d [E], key_fn: impl Fn(&E) -> K, key: &K) -> &'d [E]
+pub fn binary_search_slice<'d, E, K>(data: &'d [E], key_fn: impl Fn(&E) -> K, key: &K) -> &'d [E]
 where
     K: Ord,
 {
index 1cd170599ba5125a443ab60bf7527c381652e04d..0d2ae115cb0d29d3bf561f22f35e9314fc3a440d 100644 (file)
@@ -1,11 +1,14 @@
 //! Finding the dominators in a control-flow graph.
 //!
-//! Algorithm based on Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy,
-//! "A Simple, Fast Dominance Algorithm",
-//! Rice Computer Science TS-06-33870,
-//! <https://www.cs.rice.edu/~keith/EMBED/dom.pdf>.
+//! Algorithm based on Loukas Georgiadis,
+//! "Linear-Time Algorithms for Dominators and Related Problems",
+//! <ftp://ftp.cs.princeton.edu/techreports/2005/737.pdf>
+//!
+//! Additionally useful is the original Lengauer-Tarjan paper on this subject,
+//! "A Fast Algorithm for Finding Dominators in a Flowgraph"
+//! Thomas Lengauer and Robert Endre Tarjan.
+//! <https://www.cs.princeton.edu/courses/archive/spr03/cs423/download/dominators.pdf>
 
-use super::iterate::reverse_post_order;
 use super::ControlFlowGraph;
 use rustc_index::vec::{Idx, IndexVec};
 use std::cmp::Ordering;
 #[cfg(test)]
 mod tests;
 
-pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
-    let start_node = graph.start_node();
-    let rpo = reverse_post_order(&graph, start_node);
-    dominators_given_rpo(graph, &rpo)
+struct PreOrderFrame<Iter> {
+    pre_order_idx: PreorderIndex,
+    iter: Iter,
 }
 
-fn dominators_given_rpo<G: ControlFlowGraph>(graph: G, rpo: &[G::Node]) -> Dominators<G::Node> {
-    let start_node = graph.start_node();
-    assert_eq!(rpo[0], start_node);
+rustc_index::newtype_index! {
+    struct PreorderIndex { .. }
+}
 
+pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
     // compute the post order index (rank) for each node
     let mut post_order_rank = IndexVec::from_elem_n(0, graph.num_nodes());
-    for (index, node) in rpo.iter().rev().cloned().enumerate() {
-        post_order_rank[node] = index;
-    }
 
-    let mut immediate_dominators = IndexVec::from_elem_n(None, graph.num_nodes());
-    immediate_dominators[start_node] = Some(start_node);
-
-    let mut changed = true;
-    while changed {
-        changed = false;
-
-        for &node in &rpo[1..] {
-            let mut new_idom = None;
-            for pred in graph.predecessors(node) {
-                if immediate_dominators[pred].is_some() {
-                    // (*) dominators for `pred` have been calculated
-                    new_idom = Some(if let Some(new_idom) = new_idom {
-                        intersect(&post_order_rank, &immediate_dominators, new_idom, pred)
-                    } else {
-                        pred
-                    });
-                }
-            }
+    // We allocate capacity for the full set of nodes, because most of the time
+    // most of the nodes *are* reachable.
+    let mut parent: IndexVec<PreorderIndex, PreorderIndex> =
+        IndexVec::with_capacity(graph.num_nodes());
+
+    let mut stack = vec![PreOrderFrame {
+        pre_order_idx: PreorderIndex::new(0),
+        iter: graph.successors(graph.start_node()),
+    }];
+    let mut pre_order_to_real: IndexVec<PreorderIndex, G::Node> =
+        IndexVec::with_capacity(graph.num_nodes());
+    let mut real_to_pre_order: IndexVec<G::Node, Option<PreorderIndex>> =
+        IndexVec::from_elem_n(None, graph.num_nodes());
+    pre_order_to_real.push(graph.start_node());
+    parent.push(PreorderIndex::new(0)); // the parent of the root node is the root for now.
+    real_to_pre_order[graph.start_node()] = Some(PreorderIndex::new(0));
+    let mut post_order_idx = 0;
+
+    // Traverse the graph, collecting a number of things:
+    //
+    // * Preorder mapping (to it, and back to the actual ordering)
+    // * Postorder mapping (used exclusively for rank_partial_cmp on the final product)
+    // * Parents for each vertex in the preorder tree
+    //
+    // These are all done here rather than through one of the 'standard'
+    // graph traversals to help make this fast.
+    'recurse: while let Some(frame) = stack.last_mut() {
+        while let Some(successor) = frame.iter.next() {
+            if real_to_pre_order[successor].is_none() {
+                let pre_order_idx = pre_order_to_real.push(successor);
+                real_to_pre_order[successor] = Some(pre_order_idx);
+                parent.push(frame.pre_order_idx);
+                stack.push(PreOrderFrame { pre_order_idx, iter: graph.successors(successor) });
 
-            if new_idom != immediate_dominators[node] {
-                immediate_dominators[node] = new_idom;
-                changed = true;
+                continue 'recurse;
             }
         }
+        post_order_rank[pre_order_to_real[frame.pre_order_idx]] = post_order_idx;
+        post_order_idx += 1;
+
+        stack.pop();
     }
 
-    Dominators { post_order_rank, immediate_dominators }
-}
+    let reachable_vertices = pre_order_to_real.len();
+
+    let mut idom = IndexVec::from_elem_n(PreorderIndex::new(0), reachable_vertices);
+    let mut semi = IndexVec::from_fn_n(std::convert::identity, reachable_vertices);
+    let mut label = semi.clone();
+    let mut bucket = IndexVec::from_elem_n(vec![], reachable_vertices);
+    let mut lastlinked = None;
+
+    // We loop over vertices in reverse preorder. This implements the pseudocode
+    // of the simple Lengauer-Tarjan algorithm. A few key facts are noted here
+    // which are helpful for understanding the code (full proofs and such are
+    // found in various papers, including one cited at the top of this file).
+    //
+    // For each vertex w (which is not the root),
+    //  * semi[w] is a proper ancestor of the vertex w (i.e., semi[w] != w)
+    //  * idom[w] is an ancestor of semi[w] (i.e., idom[w] may equal semi[w])
+    //
+    // An immediate dominator of w (idom[w]) is a vertex v where v dominates w
+    // and every other dominator of w dominates v. (Every vertex except the root has
+    // a unique immediate dominator.)
+    //
+    // A semidominator for a given vertex w (semi[w]) is the vertex v with minimum
+    // preorder number such that there exists a path from v to w in which all elements (other than w) have
+    // preorder numbers greater than w (i.e., this path is not the tree path to
+    // w).
+    for w in (PreorderIndex::new(1)..PreorderIndex::new(reachable_vertices)).rev() {
+        // Optimization: process buckets just once, at the start of the
+        // iteration. Do not explicitly empty the bucket (even though it will
+        // not be used again), to save some instructions.
+        //
+        // The bucket here contains the vertices whose semidominator is the
+        // vertex w, which we are guaranteed to have found: all vertices who can
+        // be semidominated by w must have a preorder number exceeding w, so
+        // they have been placed in the bucket.
+        //
+        // We compute a partial set of immediate dominators here.
+        let z = parent[w];
+        for &v in bucket[z].iter() {
+            // This uses the result of Lemma 5 from section 2 from the original
+            // 1979 paper, to compute either the immediate or relative dominator
+            // for a given vertex v.
+            //
+            // eval returns a vertex y, for which semi[y] is minimum among
+            // vertices semi[v] +> y *> v. Note that semi[v] = z as we're in the
+            // z bucket.
+            //
+            // Given such a vertex y, semi[y] <= semi[v] and idom[y] = idom[v].
+            // If semi[y] = semi[v], though, idom[v] = semi[v].
+            //
+            // Using this, we can either set idom[v] to be:
+            //  * semi[v] (i.e. z), if semi[y] is z
+            //  * idom[y], otherwise
+            //
+            // We don't directly set to idom[y] though as it's not necessarily
+            // known yet. The second preorder traversal will cleanup by updating
+            // the idom for any that were missed in this pass.
+            let y = eval(&mut parent, lastlinked, &semi, &mut label, v);
+            idom[v] = if semi[y] < z { y } else { z };
+        }
+
+        // This loop computes the semi[w] for w.
+        semi[w] = w;
+        for v in graph.predecessors(pre_order_to_real[w]) {
+            let v = real_to_pre_order[v].unwrap();
+
+            // eval returns a vertex x from which semi[x] is minimum among
+            // vertices semi[v] +> x *> v.
+            //
+            // From Lemma 4 from section 2, we know that the semidominator of a
+            // vertex w is the minimum (by preorder number) vertex of the
+            // following:
+            //
+            //  * direct predecessors of w with preorder number less than w
+            //  * semidominators of u such that u > w and there exists (v, w)
+            //    such that u *> v
+            //
+            // This loop therefore identifies such a minima. Note that any
+            // semidominator path to w must have all but the first vertex go
+            // through vertices numbered greater than w, so the reverse preorder
+            // traversal we are using guarantees that all of the information we
+            // might need is available at this point.
+            //
+            // The eval call will give us semi[x], which is either:
+            //
+            //  * v itself, if v has not yet been processed
+            //  * A possible 'best' semidominator for w.
+            let x = eval(&mut parent, lastlinked, &semi, &mut label, v);
+            semi[w] = std::cmp::min(semi[w], semi[x]);
+        }
+        // semi[w] is now semidominator(w) and won't change any more.
 
-fn intersect<Node: Idx>(
-    post_order_rank: &IndexVec<Node, usize>,
-    immediate_dominators: &IndexVec<Node, Option<Node>>,
-    mut node1: Node,
-    mut node2: Node,
-) -> Node {
-    while node1 != node2 {
-        while post_order_rank[node1] < post_order_rank[node2] {
-            node1 = immediate_dominators[node1].unwrap();
+        // Optimization: Do not insert into buckets if parent[w] = semi[w], as
+        // we then immediately know the idom.
+        //
+        // If we don't yet know the idom directly, then push this vertex into
+        // our semidominator's bucket, where it will get processed at a later
+        // stage to compute its immediate dominator.
+        if parent[w] != semi[w] {
+            bucket[semi[w]].push(w);
+        } else {
+            idom[w] = parent[w];
         }
 
-        while post_order_rank[node2] < post_order_rank[node1] {
-            node2 = immediate_dominators[node2].unwrap();
+        // Optimization: We share the parent array between processed and not
+        // processed elements; lastlinked represents the divider.
+        lastlinked = Some(w);
+    }
+
+    // Finalize the idoms for any that were not fully settable during initial
+    // traversal.
+    //
+    // If idom[w] != semi[w] then we know that we've stored vertex y from above
+    // into idom[w]. It is known to be our 'relative dominator', which means
+    // that it's one of w's ancestors and has the same immediate dominator as w,
+    // so use that idom.
+    for w in PreorderIndex::new(1)..PreorderIndex::new(reachable_vertices) {
+        if idom[w] != semi[w] {
+            idom[w] = idom[idom[w]];
         }
     }
 
-    node1
+    let mut immediate_dominators = IndexVec::from_elem_n(None, graph.num_nodes());
+    for (idx, node) in pre_order_to_real.iter_enumerated() {
+        immediate_dominators[*node] = Some(pre_order_to_real[idom[idx]]);
+    }
+
+    Dominators { post_order_rank, immediate_dominators }
+}
+
+/// Evaluate the link-eval virtual forest, providing the currently minimum semi
+/// value for the passed `node` (which may be itself).
+///
+/// This maintains that for every vertex v, `label[v]` is such that:
+///
+/// ```text
+/// semi[eval(v)] = min { semi[label[u]] | root_in_forest(v) +> u *> v }
+/// ```
+///
+/// where `+>` is a proper ancestor and `*>` is just an ancestor.
+#[inline]
+fn eval(
+    ancestor: &mut IndexVec<PreorderIndex, PreorderIndex>,
+    lastlinked: Option<PreorderIndex>,
+    semi: &IndexVec<PreorderIndex, PreorderIndex>,
+    label: &mut IndexVec<PreorderIndex, PreorderIndex>,
+    node: PreorderIndex,
+) -> PreorderIndex {
+    if is_processed(node, lastlinked) {
+        compress(ancestor, lastlinked, semi, label, node);
+        label[node]
+    } else {
+        node
+    }
+}
+
+#[inline]
+fn is_processed(v: PreorderIndex, lastlinked: Option<PreorderIndex>) -> bool {
+    if let Some(ll) = lastlinked { v >= ll } else { false }
+}
+
+#[inline]
+fn compress(
+    ancestor: &mut IndexVec<PreorderIndex, PreorderIndex>,
+    lastlinked: Option<PreorderIndex>,
+    semi: &IndexVec<PreorderIndex, PreorderIndex>,
+    label: &mut IndexVec<PreorderIndex, PreorderIndex>,
+    v: PreorderIndex,
+) {
+    assert!(is_processed(v, lastlinked));
+    let u = ancestor[v];
+    if is_processed(u, lastlinked) {
+        compress(ancestor, lastlinked, semi, label, u);
+        if semi[label[u]] < semi[label[v]] {
+            label[v] = label[u];
+        }
+        ancestor[v] = ancestor[u];
+    }
 }
 
 #[derive(Clone, Debug)]
index 1160df5186b367d21b87f67756a23ef44d1bbb48..ff31d8f7fdcf6d5148c01a219464e6a0bb4fe22d 100644 (file)
@@ -32,3 +32,14 @@ fn paper() {
     assert_eq!(immediate_dominators[5], Some(6));
     assert_eq!(immediate_dominators[6], Some(6));
 }
+
+#[test]
+fn paper_slt() {
+    // example from the paper:
+    let graph = TestGraph::new(
+        1,
+        &[(1, 2), (1, 3), (2, 3), (2, 7), (3, 4), (3, 6), (4, 5), (5, 4), (6, 7), (7, 8), (8, 5)],
+    );
+
+    dominators(&graph);
+}
index a9db3497b23908a50548e9a24ab74f189feed37d..57007611a76c3b3d12a4fe43142eb2e3c775d284 100644 (file)
@@ -79,7 +79,7 @@ pub struct DepthFirstSearch<'graph, G>
     visited: BitSet<G::Node>,
 }
 
-impl<G> DepthFirstSearch<'graph, G>
+impl<'graph, G> DepthFirstSearch<'graph, G>
 where
     G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
 {
@@ -209,7 +209,7 @@ pub struct TriColorDepthFirstSearch<'graph, G>
     settled: BitSet<G::Node>,
 }
 
-impl<G> TriColorDepthFirstSearch<'graph, G>
+impl<'graph, G> TriColorDepthFirstSearch<'graph, G>
 where
     G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
 {
@@ -276,7 +276,7 @@ pub fn run_from<V>(mut self, root: G::Node, visitor: &mut V) -> Option<V::BreakV
     }
 }
 
-impl<G> TriColorDepthFirstSearch<'graph, G>
+impl<G> TriColorDepthFirstSearch<'_, G>
 where
     G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors + WithStartNode,
 {
index b84f28b6a9edf408999fd4dde15a3da2e90b0ef3..508a084b311f54eedb9390db8e628739f8a31c7c 100644 (file)
@@ -97,7 +97,7 @@ fn num_edges(&self) -> usize {
     }
 }
 
-impl<N: Idx, S: Idx> GraphSuccessors<'graph> for Sccs<N, S> {
+impl<'graph, N: Idx, S: Idx> GraphSuccessors<'graph> for Sccs<N, S> {
     type Item = S;
 
     type Iter = std::iter::Cloned<std::slice::Iter<'graph, S>>;
index 4ed888784181447edbf711ce2d6c1408d5a171d8..5d9bc1b2e5168791e9df752caea863da8cab5d7c 100644 (file)
@@ -94,7 +94,7 @@ fn num_edges(&self) -> usize {
     }
 }
 
-impl<N: Idx> GraphSuccessors<'graph> for VecGraph<N> {
+impl<'graph, N: Idx> GraphSuccessors<'graph> for VecGraph<N> {
     type Item = N;
 
     type Iter = std::iter::Cloned<std::slice::Iter<'graph, N>>;
index 77784bf1705234d0018ef5aca044267902dd6735..e17724b72f8b8f0dd1f18e31988ecde8b5cbc90d 100644 (file)
 #![feature(core_intrinsics)]
 #![feature(extend_one)]
 #![feature(hash_raw_entry)]
-#![feature(in_band_lifetimes)]
 #![feature(maybe_uninit_uninit_array)]
 #![feature(min_specialization)]
 #![feature(never_type)]
 #![feature(type_alias_impl_trait)]
 #![feature(new_uninit)]
-#![feature(nll)]
 #![feature(once_cell)]
 #![feature(test)]
 #![feature(thread_id_value)]
index 61c7239c55ff497b8b190257f8cec9b48512c5c0..593316e2699fade06477c3c71367cd723dcc7977 100644 (file)
@@ -84,7 +84,7 @@ pub fn get(&self, idx: I) -> Option<&(K, V)> {
     /// If there are multiple items that are equivalent to `key`, they will be yielded in
     /// insertion order.
     #[inline]
-    pub fn get_by_key(&'a self, key: K) -> impl 'a + Iterator<Item = &'a V> {
+    pub fn get_by_key(&self, key: K) -> impl Iterator<Item = &V> + '_ {
         self.get_by_key_enumerated(key).map(|(_, v)| v)
     }
 
@@ -94,7 +94,7 @@ pub fn get_by_key(&'a self, key: K) -> impl 'a + Iterator<Item = &'a V> {
     /// If there are multiple items that are equivalent to `key`, they will be yielded in
     /// insertion order.
     #[inline]
-    pub fn get_by_key_enumerated(&'a self, key: K) -> impl '_ + Iterator<Item = (I, &V)> {
+    pub fn get_by_key_enumerated(&self, key: K) -> impl Iterator<Item = (I, &V)> + '_ {
         let lower_bound = self.idx_sorted_by_item_key.partition_point(|&i| self.items[i].0 < key);
         self.idx_sorted_by_item_key[lower_bound..].iter().map_while(move |&i| {
             let (k, v) = &self.items[i];
index 2de05cd4e56794180ae27b5fb476bed3f07e101c..ec6a62016a87cffad47430832d96f4554d5567aa 100644 (file)
@@ -423,14 +423,14 @@ fn into_iter(self) -> Self::IntoIter {
 
 /// adapts Item of array reference iterator to Item of hashmap reference iterator.
 #[inline(always)]
-fn adapt_array_ref_it<K, V>(pair: &'a (K, V)) -> (&'a K, &'a V) {
+fn adapt_array_ref_it<K, V>(pair: &(K, V)) -> (&K, &V) {
     let (a, b) = pair;
     (a, b)
 }
 
 /// adapts Item of array mut reference iterator to Item of hashmap mut reference iterator.
 #[inline(always)]
-fn adapt_array_mut_it<K, V>(pair: &'a mut (K, V)) -> (&'a K, &'a mut V) {
+fn adapt_array_mut_it<K, V>(pair: &mut (K, V)) -> (&K, &mut V) {
     let (a, b) = pair;
     (a, b)
 }
index 29baf4e1ddb661c4a4a421e9c58e3b0a81e50afc..f71522d37148ad8b86217000570356f4b0540a2b 100644 (file)
@@ -75,7 +75,7 @@ pub fn is_empty(&self) -> bool {
     /// An iterator visiting all elements in arbitrary order.
     /// The iterator element type is `&'a T`.
     #[inline]
-    pub fn iter(&'a self) -> impl Iterator<Item = &'a T> {
+    pub fn iter(&self) -> impl Iterator<Item = &T> {
         self.into_iter()
     }
 
index 1cf030d852e9fc8153388b6897a55da453bccf02..ce60d40b24b44e1087a5428c02d214c0379f8e89 100644 (file)
@@ -2,8 +2,8 @@
 
 pub fn iter<Ls>(
     first: Option<Ls::LinkIndex>,
-    links: &'a Ls,
-) -> impl Iterator<Item = Ls::LinkIndex> + 'a
+    links: &Ls,
+) -> impl Iterator<Item = Ls::LinkIndex> + '_
 where
     Ls: Links,
 {
index 1b123520961a6601106571a5d53e43dde4a31f33..5221ab4b6133a67a228d26c3d6a9bcde24f22c60 100644 (file)
@@ -402,7 +402,24 @@ fn process_cfg_attr(&mut self, attr: Attribute) -> Vec<Attribute> {
                 );
                 trees.push((bracket_group, Spacing::Alone));
                 let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees)));
-                self.process_cfg_attr(attr::mk_attr_from_item(item, tokens, attr.style, span))
+                let attr = attr::mk_attr_from_item(item, tokens, attr.style, span);
+                if attr.has_name(sym::crate_type) {
+                    self.sess.parse_sess.buffer_lint(
+                        rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
+                        attr.span,
+                        ast::CRATE_NODE_ID,
+                        "`crate_type` within an `#![cfg_attr] attribute is deprecated`",
+                    );
+                }
+                if attr.has_name(sym::crate_name) {
+                    self.sess.parse_sess.buffer_lint(
+                        rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
+                        attr.span,
+                        ast::CRATE_NODE_ID,
+                        "`crate_name` within an `#![cfg_attr] attribute is deprecated`",
+                    );
+                }
+                self.process_cfg_attr(attr)
             })
             .collect()
     }
index 18bc5bb64d6de781044f3547186563763863542c..6b1ef598b45539008e7f546775f7150d2045db4b 100644 (file)
@@ -10,7 +10,7 @@
 use rustc_hir::{GenericBound, PatKind, RangeEnd, TraitBoundModifier};
 use rustc_span::source_map::{SourceMap, Spanned};
 use rustc_span::symbol::{kw, Ident, IdentPrinter, Symbol};
-use rustc_span::{self, BytePos, FileName};
+use rustc_span::{self, FileName};
 use rustc_target::spec::abi::Abi;
 
 use std::borrow::Cow;
@@ -241,36 +241,6 @@ pub fn enum_def_to_string(
 }
 
 impl<'a> State<'a> {
-    pub fn cbox(&mut self, u: usize) {
-        self.s.cbox(u);
-    }
-
-    pub fn nbsp(&mut self) {
-        self.s.word(" ")
-    }
-
-    pub fn word_nbsp<S: Into<Cow<'static, str>>>(&mut self, w: S) {
-        self.s.word(w);
-        self.nbsp()
-    }
-
-    pub fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) {
-        let w = w.into();
-        // outer-box is consistent
-        self.cbox(INDENT_UNIT);
-        // head-box is inconsistent
-        self.ibox(w.len() + 1);
-        // keyword that starts the head
-        if !w.is_empty() {
-            self.word_nbsp(w);
-        }
-    }
-
-    pub fn bopen(&mut self) {
-        self.s.word("{");
-        self.end(); // close the head-box
-    }
-
     pub fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
         self.maybe_print_comment(span.hi());
         self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize));
@@ -284,33 +254,6 @@ pub fn bclose(&mut self, span: rustc_span::Span) {
         self.bclose_maybe_open(span, true)
     }
 
-    pub fn space_if_not_bol(&mut self) {
-        if !self.s.is_beginning_of_line() {
-            self.s.space();
-        }
-    }
-
-    pub fn break_offset_if_not_bol(&mut self, n: usize, off: isize) {
-        if !self.s.is_beginning_of_line() {
-            self.s.break_offset(n, off)
-        } else if off != 0 && self.s.last_token().is_hardbreak_tok() {
-            // We do something pretty sketchy here: tuck the nonzero
-            // offset-adjustment we were going to deposit along with the
-            // break into the previous hardbreak.
-            self.s.replace_last_token(pp::Printer::hardbreak_tok_offset(off));
-        }
-    }
-
-    // Synthesizes a comment that was not textually present in the original source
-    // file.
-    pub fn synth_comment(&mut self, text: String) {
-        self.s.word("/*");
-        self.s.space();
-        self.s.word(text);
-        self.s.space();
-        self.s.word("*/")
-    }
-
     pub fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
     where
         F: FnMut(&mut State<'_>, &T),
@@ -2174,7 +2117,7 @@ fn print_closure_params(&mut self, decl: &hir::FnDecl<'_>, body_id: hir::BodyId)
         match decl.output {
             hir::FnRetTy::Return(ref ty) => {
                 self.print_type(&ty);
-                self.maybe_print_comment(ty.span.lo())
+                self.maybe_print_comment(ty.span.lo());
             }
             hir::FnRetTy::DefaultReturn(..) => unreachable!(),
         }
@@ -2368,7 +2311,7 @@ pub fn print_fn_output(&mut self, decl: &hir::FnDecl<'_>) {
         self.end();
 
         if let hir::FnRetTy::Return(ref output) = decl.output {
-            self.maybe_print_comment(output.span.lo())
+            self.maybe_print_comment(output.span.lo());
         }
     }
 
@@ -2408,29 +2351,6 @@ pub fn print_ty_fn(
         self.end();
     }
 
-    pub fn maybe_print_trailing_comment(
-        &mut self,
-        span: rustc_span::Span,
-        next_pos: Option<BytePos>,
-    ) {
-        if let Some(cmnts) = self.comments() {
-            if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) {
-                self.print_comment(&cmnt);
-            }
-        }
-    }
-
-    pub fn print_remaining_comments(&mut self) {
-        // If there aren't any remaining comments, then we need to manually
-        // make sure there is a line break at the end.
-        if self.next_comment().is_none() {
-            self.s.hardbreak();
-        }
-        while let Some(ref cmnt) = self.next_comment() {
-            self.print_comment(cmnt)
-        }
-    }
-
     pub fn print_fn_header_info(&mut self, header: hir::FnHeader, vis: &hir::Visibility<'_>) {
         self.s.word(visibility_qualified(vis, ""));
 
index 32c02033dc9b93e4c1f77abbe944ce5d2774c5b5..9cf6cde259150a433041cbdb7631d194f04d736a 100644 (file)
@@ -1,6 +1,5 @@
 use crate::infer::type_variable::TypeVariableOriginKind;
-use crate::infer::InferCtxt;
-use crate::rustc_middle::ty::TypeFoldable;
+use crate::infer::{InferCtxt, Symbol};
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Namespace};
@@ -11,7 +10,7 @@
 use rustc_middle::infer::unify_key::ConstVariableOriginKind;
 use rustc_middle::ty::print::Print;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, DefIdTree, InferConst, Ty, TyCtxt};
+use rustc_middle::ty::{self, Const, DefIdTree, InferConst, Ty, TyCtxt, TypeFoldable, TypeFolder};
 use rustc_span::symbol::kw;
 use rustc_span::Span;
 use std::borrow::Cow;
@@ -306,6 +305,15 @@ pub enum UnderspecifiedArgKind {
     Const { is_parameter: bool },
 }
 
+impl UnderspecifiedArgKind {
+    fn descr(&self) -> &'static str {
+        match self {
+            Self::Type { .. } => "type",
+            Self::Const { .. } => "const",
+        }
+    }
+}
+
 impl InferenceDiagnosticsData {
     /// Generate a label for a generic argument which can't be inferred. When not
     /// much is known about the argument, `use_diag` may be used to describe the
@@ -588,6 +596,7 @@ pub fn emit_inference_failure_err(
             }
         }
 
+        let param_type = arg_data.kind.descr();
         let suffix = match local_visitor.found_node_ty {
             Some(ty) if ty.is_closure() => {
                 let substs =
@@ -626,13 +635,15 @@ pub fn emit_inference_failure_err(
             }
             Some(ty) if is_named_and_not_impl_trait(ty) && arg_data.name == "_" => {
                 let ty = ty_to_string(ty);
-                format!("the explicit type `{}`, with the type parameters specified", ty)
+                format!("the explicit type `{}`, with the {} parameters specified", ty, param_type)
             }
             Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != arg_data.name => {
+                let ty = ResolvedTypeParamEraser::new(self.tcx).fold_ty(ty);
+                let ty = ErrTypeParamEraser(self.tcx).fold_ty(ty);
                 let ty = ty_to_string(ty);
                 format!(
-                    "the explicit type `{}`, where the type parameter `{}` is specified",
-                    ty, arg_data.name,
+                    "the explicit type `{}`, where the {} parameter `{}` is specified",
+                    ty, param_type, arg_data.name,
                 )
             }
             _ => "a type".to_string(),
@@ -908,3 +919,117 @@ pub fn need_type_info_err_in_generator(
         err
     }
 }
+
+/// Turn *resolved* type params into `[type error]` to signal we don't want to display them. After
+/// performing that replacement, we'll turn all remaining infer type params to use their name from
+/// their definition, and replace all the `[type error]`s back to being infer so they display in
+/// the output as `_`. If we didn't go through `[type error]`, we would either show all type params
+/// by their name *or* `_`, neither of which is desireable: we want to show all types that we could
+/// infer as `_` to reduce verbosity and avoid telling the user about unnecessary type annotations.
+struct ResolvedTypeParamEraser<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    level: usize,
+}
+
+impl<'tcx> ResolvedTypeParamEraser<'tcx> {
+    fn new(tcx: TyCtxt<'tcx>) -> Self {
+        ResolvedTypeParamEraser { tcx, level: 0 }
+    }
+
+    /// Replace not yet inferred const params with their def name.
+    fn replace_infers(&self, c: &'tcx Const<'tcx>, index: u32, name: Symbol) -> &'tcx Const<'tcx> {
+        match c.val {
+            ty::ConstKind::Infer(..) => self.tcx().mk_const_param(index, name, c.ty),
+            _ => c,
+        }
+    }
+}
+
+impl<'tcx> TypeFolder<'tcx> for ResolvedTypeParamEraser<'tcx> {
+    fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        self.level += 1;
+        let t = match t.kind() {
+            // We'll hide this type only if all its type params are hidden as well.
+            ty::Adt(def, substs) => {
+                let generics = self.tcx().generics_of(def.did);
+                // Account for params with default values, like `Vec`, where we
+                // want to show `Vec<T>`, not `Vec<T, _>`. If we replaced that
+                // subst, then we'd get the incorrect output, so we passthrough.
+                let substs: Vec<_> = substs
+                    .iter()
+                    .zip(generics.params.iter())
+                    .map(|(subst, param)| match &(subst.unpack(), &param.kind) {
+                        (_, ty::GenericParamDefKind::Type { has_default: true, .. }) => subst,
+                        (crate::infer::GenericArgKind::Const(c), _) => {
+                            self.replace_infers(c, param.index, param.name).into()
+                        }
+                        _ => subst.super_fold_with(self),
+                    })
+                    .collect();
+                let should_keep = |subst: &GenericArg<'_>| match subst.unpack() {
+                    ty::subst::GenericArgKind::Type(t) => match t.kind() {
+                        ty::Error(_) => false,
+                        _ => true,
+                    },
+                    // Account for `const` params here, otherwise `doesnt_infer.rs`
+                    // shows `_` instead of `Foo<{ _: u32 }>`
+                    ty::subst::GenericArgKind::Const(_) => true,
+                    _ => false,
+                };
+                if self.level == 1 || substs.iter().any(should_keep) {
+                    let substs = self.tcx().intern_substs(&substs[..]);
+                    self.tcx().mk_ty(ty::Adt(def, substs))
+                } else {
+                    self.tcx().ty_error()
+                }
+            }
+            ty::Ref(_, ty, _) => {
+                let ty = self.fold_ty(ty);
+                match ty.kind() {
+                    // Avoid `&_`, these can be safely presented as `_`.
+                    ty::Error(_) => self.tcx().ty_error(),
+                    _ => t.super_fold_with(self),
+                }
+            }
+            // We could account for `()` if we wanted to replace it, but it's assured to be short.
+            ty::Tuple(_)
+            | ty::Slice(_)
+            | ty::RawPtr(_)
+            | ty::FnDef(..)
+            | ty::FnPtr(_)
+            | ty::Opaque(..)
+            | ty::Projection(_)
+            | ty::Never => t.super_fold_with(self),
+            ty::Array(ty, c) => self
+                .tcx()
+                .mk_ty(ty::Array(self.fold_ty(ty), self.replace_infers(c, 0, Symbol::intern("N")))),
+            // We don't want to hide type params that haven't been resolved yet.
+            // This would be the type that will be written out with the type param
+            // name in the output.
+            ty::Infer(_) => t,
+            // We don't want to hide the outermost type, only its type params.
+            _ if self.level == 1 => t.super_fold_with(self),
+            // Hide this type
+            _ => self.tcx().ty_error(),
+        };
+        self.level -= 1;
+        t
+    }
+}
+
+/// Replace `[type error]` with `ty::Infer(ty::Var)` to display `_`.
+struct ErrTypeParamEraser<'tcx>(TyCtxt<'tcx>);
+impl<'tcx> TypeFolder<'tcx> for ErrTypeParamEraser<'tcx> {
+    fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+        self.0
+    }
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        match t.kind() {
+            ty::Error(_) => self.tcx().mk_ty_var(ty::TyVid::from_u32(0)),
+            _ => t.super_fold_with(self),
+        }
+    }
+}
index 89c14bcc2ce2bd76beac8d879eddfac2f3e73afa..d9ab4ae1eda295ed86911ebeed4b6e5555589431 100644 (file)
@@ -461,10 +461,6 @@ fn instantiate_opaque_types_in_map<T: TypeFoldable<'tcx>>(&mut self, value: T) -
                     if let Some(def_id) = def_id.as_local() {
                         let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
                         let parent_def_id = self.infcx.defining_use_anchor;
-                        let def_scope_default = || {
-                            let opaque_parent_hir_id = tcx.hir().get_parent_item(opaque_hir_id);
-                            parent_def_id == tcx.hir().local_def_id(opaque_parent_hir_id)
-                        };
                         let (in_definition_scope, origin) = match tcx.hir().expect_item(def_id).kind
                         {
                             // Anonymous `impl Trait`
@@ -481,7 +477,14 @@ fn instantiate_opaque_types_in_map<T: TypeFoldable<'tcx>>(&mut self, value: T) -
                             }) => {
                                 (may_define_opaque_type(tcx, parent_def_id, opaque_hir_id), origin)
                             }
-                            _ => (def_scope_default(), hir::OpaqueTyOrigin::TyAlias),
+                            ref itemkind => {
+                                span_bug!(
+                                    self.value_span,
+                                    "weird opaque type: {:#?}, {:#?}",
+                                    ty.kind(),
+                                    itemkind
+                                )
+                            }
                         };
                         if in_definition_scope {
                             let opaque_type_key =
index 6147311af6159a0e36616714b4d8b67f87ed6b01..c651feaaa66f62cf5b0d1718ce6eb193bc5833d0 100644 (file)
@@ -651,7 +651,6 @@ macro_rules! untracked {
     untracked!(dump_mir_dir, String::from("abc"));
     untracked!(dump_mir_exclude_pass_number, true);
     untracked!(dump_mir_graphviz, true);
-    untracked!(emit_future_incompat_report, true);
     untracked!(emit_stack_sizes, true);
     untracked!(future_incompat_test, true);
     untracked!(hir_stats, true);
index c1a53c34b7a244d27cfbcdb8bc8289108b5debf4..c9294c68a7dc9b4999c73143d451f49edc92d3a5 100644 (file)
     "detects large moves or copies",
 }
 
+declare_lint! {
+    /// The `deprecated_cfg_attr_crate_type_name` lint detects uses of the
+    /// `#![cfg_attr(..., crate_type = "...")]` and
+    /// `#![cfg_attr(..., crate_name = "...")]` attributes to conditionally
+    /// specify the crate type and name in the source code.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![cfg_attr(debug_assertions, crate_type = "lib")]
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    ///
+    /// ### Explanation
+    ///
+    /// The `#![crate_type]` and `#![crate_name]` attributes require a hack in
+    /// the compiler to be able to change the used crate type and crate name
+    /// after macros have been expanded. Neither attribute works in combination
+    /// with Cargo as it explicitly passes `--crate-type` and `--crate-name` on
+    /// the commandline. These values must match the value used in the source
+    /// code to prevent an error.
+    ///
+    /// To fix the warning use `--crate-type` on the commandline when running
+    /// rustc instead of `#![cfg_attr(..., crate_type = "...")]` and
+    /// `--crate-name` instead of `#![cfg_attr(..., crate_name = "...")]`.
+    pub DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
+    Warn,
+    "detects usage of `#![cfg_attr(..., crate_type/crate_name = \"...\")]`",
+    @future_incompatible = FutureIncompatibleInfo {
+        reference: "issue #91632 <https://github.com/rust-lang/rust/issues/91632>",
+    };
+}
+
 declare_lint_pass! {
     /// Does nothing as a lint pass, but registers some `Lint`s
     /// that are used by other parts of the compiler.
         NON_EXHAUSTIVE_OMITTED_PATTERNS,
         TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
         DEREF_INTO_DYN_SUPERTRAIT,
+        DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
     ]
 }
 
index 943ce589c4f36ea41a19888cbe61af07159d0c40..3b6808d693f2176bfa8a8b48194face0ededc109 100644 (file)
@@ -276,8 +276,11 @@ fn main() {
         "stdc++"
     };
 
-    // RISC-V requires libatomic for sub-word atomic operations
-    if target.starts_with("riscv") {
+    // RISC-V GCC erroneously requires libatomic for sub-word
+    // atomic operations. FreeBSD uses Clang as its system
+    // compiler and provides no libatomic in its base system so
+    // does not want this.
+    if !target.contains("freebsd") && target.starts_with("riscv") {
         println!("cargo:rustc-link-lib=atomic");
     }
 
index 75edcaadfdff26adf39425a16d88f3531ca2f496..d4530883b6a102bc44ad24f3bcb16949ad51d424 100644 (file)
@@ -1519,6 +1519,7 @@ pub fn make_nop(&mut self) {
     }
 
     /// Changes a statement to a nop and returns the original statement.
+    #[must_use = "If you don't need the statement, use `make_nop` instead"]
     pub fn replace_nop(&mut self) -> Self {
         Statement {
             source_info: self.source_info,
index a82f98d28e723f8dbfd19258ee1771919d105ec9..d52b6a8bc75872d743b38391d18e42fec9ce579c 100644 (file)
@@ -78,6 +78,13 @@ pub fn all_targets(&self) -> &[BasicBlock] {
     pub fn all_targets_mut(&mut self) -> &mut [BasicBlock] {
         &mut self.targets
     }
+
+    /// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
+    /// specific value.  This cannot fail, as it'll return the `otherwise`
+    /// branch if there's not a specific match for the value.
+    pub fn target_for_value(&self, value: u128) -> BasicBlock {
+        self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
+    }
 }
 
 pub struct SwitchTargetsIter<'a> {
index 4b38105e44717e52300e5b6f9e789e58f8093d12..2d301262730642aeaefefc3a59f290a82ba507f8 100644 (file)
@@ -7,6 +7,7 @@
 use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_hir::lang_items::LangItem;
 use rustc_macros::HashStable;
+use rustc_middle::ty::normalize_erasing_regions::NormalizationError;
 
 use std::fmt;
 
@@ -575,6 +576,23 @@ pub fn subst_mir_and_normalize_erasing_regions<T>(
         }
     }
 
+    #[inline(always)]
+    pub fn try_subst_mir_and_normalize_erasing_regions<T>(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        v: T,
+    ) -> Result<T, NormalizationError<'tcx>>
+    where
+        T: TypeFoldable<'tcx> + Clone,
+    {
+        if let Some(substs) = self.substs_for_mir_body() {
+            tcx.try_subst_and_normalize_erasing_regions(substs, param_env, v)
+        } else {
+            tcx.try_normalize_erasing_regions(param_env, v)
+        }
+    }
+
     /// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by
     /// identity parameters if they are determined to be unused in `instance.def`.
     pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self {
index c0e1360640fd2647227c8a9946ef0d0d1eecdfbc..c472d4a5a4dc2524762c8dc55076edbd4dd7f9f5 100644 (file)
@@ -115,6 +115,8 @@ pub fn normalize_erasing_late_bound_regions<T>(
     /// Monomorphizes a type from the AST by first applying the
     /// in-scope substitutions and then normalizing any associated
     /// types.
+    /// Panics if normalization fails. In case normalization might fail
+    /// use `try_subst_and_normalize_erasing_regions` instead.
     pub fn subst_and_normalize_erasing_regions<T>(
         self,
         param_substs: SubstsRef<'tcx>,
@@ -134,6 +136,30 @@ pub fn subst_and_normalize_erasing_regions<T>(
         let substituted = value.subst(self, param_substs);
         self.normalize_erasing_regions(param_env, substituted)
     }
+
+    /// Monomorphizes a type from the AST by first applying the
+    /// in-scope substitutions and then trying to normalize any associated
+    /// types. Contrary to `subst_and_normalize_erasing_regions` this does
+    /// not assume that normalization succeeds.
+    pub fn try_subst_and_normalize_erasing_regions<T>(
+        self,
+        param_substs: SubstsRef<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        value: T,
+    ) -> Result<T, NormalizationError<'tcx>>
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        debug!(
+            "subst_and_normalize_erasing_regions(\
+             param_substs={:?}, \
+             value={:?}, \
+             param_env={:?})",
+            param_substs, value, param_env,
+        );
+        let substituted = value.subst(self, param_substs);
+        self.try_normalize_erasing_regions(param_env, substituted)
+    }
 }
 
 struct NormalizeAfterErasingRegionsFolder<'tcx> {
index 84ae2a2fbd094efcfdc0053bfebee2344e672fbf..28a5a22dd9d5d1558acf329222a4366d26462e47 100644 (file)
@@ -34,7 +34,7 @@ fn is_stable(place: PlaceRef<'_>) -> bool {
 }
 
 /// Determine whether this type may be a reference (or box), and thus needs retagging.
-fn may_be_reference(ty: Ty<'tcx>) -> bool {
+fn may_be_reference(ty: Ty<'_>) -> bool {
     match ty.kind() {
         // Primitive types that are not references
         ty::Bool
index 6f0d03068f566aa4b4b742bef3ba0fb4bea089ce..a19a3c8b1d50609b4712eee6683bd8090507da79 100644 (file)
@@ -23,7 +23,7 @@ struct ConstMutationChecker<'a, 'tcx> {
     target_local: Option<Local>,
 }
 
-impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> {
+impl<'tcx> ConstMutationChecker<'_, 'tcx> {
     fn is_const_item(&self, local: Local) -> Option<DefId> {
         if let Some(box LocalInfo::ConstRef { def_id }) = self.body.local_decls[local].local_info {
             Some(def_id)
@@ -95,7 +95,7 @@ fn lint_const_item_usage(
     }
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for ConstMutationChecker<'_, 'tcx> {
     fn visit_statement(&mut self, stmt: &Statement<'tcx>, loc: Location) {
         if let StatementKind::Assign(box (lhs, _)) = &stmt.kind {
             // Check for assignment to fields of a constant
index 31d547103095f7f6e9fe2e2b73cbf70facbeee5b..c6661e9c74e9b0a51d280474fce5aa991c1a118f 100644 (file)
@@ -66,7 +66,7 @@ fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
     }
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for PackedRefChecker<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
         // Make sure we know where in the MIR we are.
         self.source_info = terminator.source_info;
index cdfeb957df9b1f1c38eff3d3a1dcf35e35ae8141..2dda19badd7c15afd23480b45e53986bd66fab1a 100644 (file)
@@ -46,7 +46,7 @@ fn new(
     }
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
         self.source_info = terminator.source_info;
         match terminator.kind {
@@ -244,7 +244,7 @@ fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location:
     }
 }
 
-impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
+impl<'tcx> UnsafetyChecker<'_, 'tcx> {
     fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) {
         // Violations can turn out to be `UnsafeFn` during analysis, but they should not start out as such.
         assert_ne!(kind, UnsafetyViolationKind::UnsafeFn);
@@ -397,7 +397,7 @@ struct UnusedUnsafeVisitor<'a> {
     unsafe_blocks: &'a mut Vec<(hir::HirId, bool)>,
 }
 
-impl<'a, 'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a> {
+impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_> {
     type Map = intravisit::ErasedMap<'tcx>;
 
     fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
index 3613fa4560d6c1ebbe9a947858977c317cce259f..839d94167fecdfbc90717b6849c745c781c211af 100644 (file)
@@ -89,7 +89,7 @@ fn find_optimization_oportunities<'tcx>(body: &Body<'tcx>) -> Vec<(Local, Consta
     eligable_locals
 }
 
-impl<'tcx> Visitor<'tcx> for LocalUseVisitor {
+impl Visitor<'_> for LocalUseVisitor {
     fn visit_local(&mut self, local: &Local, context: PlaceContext, location: Location) {
         if context.is_mutating_use() {
             self.local_mutating_uses[*local] = self.local_mutating_uses[*local].saturating_add(1);
index beb158dd25828da7aa54f748d6ac1786f13a69ac..905173b0457191022d8288a3bbdf012ecf7b2742 100644 (file)
@@ -54,7 +54,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     }
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for ConstGotoOptimizationFinder<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for ConstGotoOptimizationFinder<'_, 'tcx> {
     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
         let _: Option<_> = try {
             let target = terminator.kind.as_goto()?;
@@ -83,20 +83,7 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
                     // Now find which value in the Switch matches the const value.
                     let const_value =
                         _const.literal.try_eval_bits(self.tcx, self.param_env, switch_ty)?;
-                    let found_value_idx_option = targets
-                        .iter()
-                        .enumerate()
-                        .find(|(_, (value, _))| const_value == *value)
-                        .map(|(idx, _)| idx);
-
-                    let target_to_use_in_goto =
-                        if let Some(found_value_idx) = found_value_idx_option {
-                            targets.iter().nth(found_value_idx).unwrap().1
-                        } else {
-                            // If we did not find the const value in values, it must be the otherwise case
-                            targets.otherwise()
-                        };
-
+                    let target_to_use_in_goto = targets.target_for_value(const_value);
                     self.optimizations.push(OptimizationToApply {
                         bb_with_goto: location.block,
                         target_to_use_in_goto,
index e3377f5953aadace7d689525e01769274a384dcd..e897e89982c0d8ad0518acdfdc405d5527c360f5 100644 (file)
@@ -171,7 +171,7 @@ struct ConstPropMachine<'mir, 'tcx> {
     can_const_prop: IndexVec<Local, ConstPropMode>,
 }
 
-impl<'mir, 'tcx> ConstPropMachine<'mir, 'tcx> {
+impl ConstPropMachine<'_, '_> {
     fn new(
         only_propagate_inside_block_locals: BitSet<Local>,
         can_const_prop: IndexVec<Local, ConstPropMode>,
@@ -308,14 +308,14 @@ fn init_frame_extra(
     }
 
     #[inline(always)]
-    fn stack(
+    fn stack<'a>(
         ecx: &'a InterpCx<'mir, 'tcx, Self>,
     ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
         &ecx.machine.stack
     }
 
     #[inline(always)]
-    fn stack_mut(
+    fn stack_mut<'a>(
         ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
     ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
         &mut ecx.machine.stack
@@ -336,7 +336,7 @@ struct ConstPropagator<'mir, 'tcx> {
     source_info: Option<SourceInfo>,
 }
 
-impl<'mir, 'tcx> LayoutOfHelpers<'tcx> for ConstPropagator<'mir, 'tcx> {
+impl<'tcx> LayoutOfHelpers<'tcx> for ConstPropagator<'_, 'tcx> {
     type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
 
     #[inline]
@@ -345,21 +345,21 @@ fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> Lay
     }
 }
 
-impl<'mir, 'tcx> HasDataLayout for ConstPropagator<'mir, 'tcx> {
+impl HasDataLayout for ConstPropagator<'_, '_> {
     #[inline]
     fn data_layout(&self) -> &TargetDataLayout {
         &self.tcx.data_layout
     }
 }
 
-impl<'mir, 'tcx> ty::layout::HasTyCtxt<'tcx> for ConstPropagator<'mir, 'tcx> {
+impl<'tcx> ty::layout::HasTyCtxt<'tcx> for ConstPropagator<'_, 'tcx> {
     #[inline]
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
 }
 
-impl<'mir, 'tcx> ty::layout::HasParamEnv<'tcx> for ConstPropagator<'mir, 'tcx> {
+impl<'tcx> ty::layout::HasParamEnv<'tcx> for ConstPropagator<'_, 'tcx> {
     #[inline]
     fn param_env(&self) -> ty::ParamEnv<'tcx> {
         self.param_env
@@ -971,7 +971,7 @@ struct CanConstProp {
 
 impl CanConstProp {
     /// Returns true if `local` can be propagated
-    fn check(
+    fn check<'tcx>(
         tcx: TyCtxt<'tcx>,
         param_env: ParamEnv<'tcx>,
         body: &Body<'tcx>,
@@ -1019,7 +1019,7 @@ fn check(
     }
 }
 
-impl<'tcx> Visitor<'tcx> for CanConstProp {
+impl Visitor<'_> for CanConstProp {
     fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
         use rustc_middle::mir::visit::PlaceContext::*;
         match context {
@@ -1079,7 +1079,7 @@ fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
     }
 }
 
-impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
+impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
index 513a85b591306bec225b4ac706fbe7b3734b7ae9..588103ca43dd0fdc5e058f747c1f502c5ae74134 100644 (file)
@@ -629,7 +629,7 @@ pub fn alert_on_unused_expressions(&self, debug_counters: &DebugCounters) {
 }
 
 /// Generates the MIR pass `CoverageSpan`-specific spanview dump file.
-pub(super) fn dump_coverage_spanview(
+pub(super) fn dump_coverage_spanview<'tcx>(
     tcx: TyCtxt<'tcx>,
     mir_body: &mir::Body<'tcx>,
     basic_coverage_blocks: &CoverageGraph,
@@ -651,7 +651,7 @@ pub(super) fn dump_coverage_spanview(
 }
 
 /// Converts the computed `BasicCoverageBlockData`s into `SpanViewable`s.
-fn span_viewables(
+fn span_viewables<'tcx>(
     tcx: TyCtxt<'tcx>,
     mir_body: &mir::Body<'tcx>,
     basic_coverage_blocks: &CoverageGraph,
@@ -670,7 +670,7 @@ fn span_viewables(
 }
 
 /// Generates the MIR pass coverage-specific graphviz dump file.
-pub(super) fn dump_coverage_graphviz(
+pub(super) fn dump_coverage_graphviz<'tcx>(
     tcx: TyCtxt<'tcx>,
     mir_body: &mir::Body<'tcx>,
     pass_name: &str,
@@ -750,7 +750,7 @@ pub(super) fn dump_coverage_graphviz(
         .expect("Unexpected error writing BasicCoverageBlock graphviz DOT file");
 }
 
-fn bcb_to_string_sections(
+fn bcb_to_string_sections<'tcx>(
     tcx: TyCtxt<'tcx>,
     mir_body: &mir::Body<'tcx>,
     debug_counters: &DebugCounters,
@@ -817,7 +817,7 @@ fn bcb_to_string_sections(
 
 /// Returns a simple string representation of a `TerminatorKind` variant, independent of any
 /// values it might hold.
-pub(super) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str {
+pub(super) fn term_type(kind: &TerminatorKind<'_>) -> &'static str {
     match kind {
         TerminatorKind::Goto { .. } => "Goto",
         TerminatorKind::SwitchInt { .. } => "SwitchInt",
index d78ad6ce97fa23697cb768ba90ed40efecaa81df..a25402a1ff9f54674fc6a985a1e8f7bf4d224b8c 100644 (file)
@@ -27,7 +27,7 @@ pub(super) struct CoverageGraph {
 }
 
 impl CoverageGraph {
-    pub fn from_mir(mir_body: &mir::Body<'tcx>) -> Self {
+    pub fn from_mir(mir_body: &mir::Body<'_>) -> Self {
         let (bcbs, bb_to_bcb) = Self::compute_basic_coverage_blocks(mir_body);
 
         // Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock
@@ -74,7 +74,7 @@ pub fn from_mir(mir_body: &mir::Body<'tcx>) -> Self {
     }
 
     fn compute_basic_coverage_blocks(
-        mir_body: &mir::Body<'tcx>,
+        mir_body: &mir::Body<'_>,
     ) -> (
         IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
         IndexVec<BasicBlock, Option<BasicCoverageBlock>>,
@@ -267,7 +267,7 @@ fn successors(&self, node: Self::Node) -> <Self as GraphSuccessors<'_>>::Iter {
     }
 }
 
-impl graph::GraphPredecessors<'graph> for CoverageGraph {
+impl<'graph> graph::GraphPredecessors<'graph> for CoverageGraph {
     type Item = BasicCoverageBlock;
     type Iter = std::iter::Copied<std::slice::Iter<'graph, BasicCoverageBlock>>;
 }
index bba188bd3935075a77b6788c25f0c69c39119fec..b009e2fd0e4ade6889dd9c653e9951586c1355d2 100644 (file)
@@ -443,7 +443,7 @@ fn format_counter(&self, counter_kind: &CoverageKind) -> String {
 }
 
 fn inject_edge_counter_basic_block(
-    mir_body: &mut mir::Body<'tcx>,
+    mir_body: &mut mir::Body<'_>,
     from_bb: BasicBlock,
     to_bb: BasicBlock,
 ) -> BasicBlock {
@@ -466,7 +466,7 @@ fn inject_edge_counter_basic_block(
 }
 
 fn inject_statement(
-    mir_body: &mut mir::Body<'tcx>,
+    mir_body: &mut mir::Body<'_>,
     counter_kind: CoverageKind,
     bb: BasicBlock,
     some_code_region: Option<CodeRegion>,
@@ -488,7 +488,7 @@ fn inject_statement(
 }
 
 // Non-code expressions are injected into the coverage map, without generating executable code.
-fn inject_intermediate_expression(mir_body: &mut mir::Body<'tcx>, expression: CoverageKind) {
+fn inject_intermediate_expression(mir_body: &mut mir::Body<'_>, expression: CoverageKind) {
     debug_assert!(matches!(expression, CoverageKind::Expression { .. }));
     debug!("  injecting non-code expression {:?}", expression);
     let inject_in_bb = mir::START_BLOCK;
index 760f16eae6b1f85be18bea6412b0f260c77b4ed8..1721fb5cde0e899e04c5acb82684c7803ed1d6d1 100644 (file)
@@ -137,7 +137,7 @@ fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) ->
     coverage_visitor.info
 }
 
-fn covered_file_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Symbol> {
+fn covered_file_name(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> {
     if tcx.is_mir_available(def_id) {
         let body = mir_body(tcx, def_id);
         for bb_data in body.basic_blocks().iter() {
index d13fa0729cd9562a0306ef1be170dd3a302fb3a4..146cabf3508806c167ef755b825d2e2cafbd3fb2 100644 (file)
@@ -21,7 +21,7 @@ pub(super) enum CoverageStatement {
 }
 
 impl CoverageStatement {
-    pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String {
+    pub fn format<'tcx>(&self, tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>) -> String {
         match *self {
             Self::Statement(bb, span, stmt_index) => {
                 let stmt = &mir_body[bb].statements[stmt_index];
@@ -86,7 +86,7 @@ pub fn for_fn_sig(fn_sig_span: Span) -> Self {
     }
 
     pub fn for_statement(
-        statement: &Statement<'tcx>,
+        statement: &Statement<'_>,
         span: Span,
         expn_span: Span,
         bcb: BasicCoverageBlock,
@@ -151,7 +151,7 @@ pub fn is_in_same_bcb(&self, other: &Self) -> bool {
         self.bcb == other.bcb
     }
 
-    pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String {
+    pub fn format<'tcx>(&self, tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>) -> String {
         format!(
             "{}\n    {}",
             source_range_no_file(tcx, &self.span),
@@ -159,10 +159,10 @@ pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String
         )
     }
 
-    pub fn format_coverage_statements(
+    pub fn format_coverage_statements<'tcx>(
         &self,
         tcx: TyCtxt<'tcx>,
-        mir_body: &'a mir::Body<'tcx>,
+        mir_body: &mir::Body<'tcx>,
     ) -> String {
         let mut sorted_coverage_statements = self.coverage_statements.clone();
         sorted_coverage_statements.sort_unstable_by_key(|covstmt| match *covstmt {
@@ -803,7 +803,7 @@ fn span_bcb_is_dominated_by(&self, covspan: &CoverageSpan, dom_covspan: &Coverag
 
 /// If the MIR `Statement` has a span contributive to computing coverage spans,
 /// return it; otherwise return `None`.
-pub(super) fn filtered_statement_span(statement: &'a Statement<'tcx>) -> Option<Span> {
+pub(super) fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
     match statement.kind {
         // These statements have spans that are often outside the scope of the executed source code
         // for their parent `BasicBlock`.
@@ -847,7 +847,7 @@ pub(super) fn filtered_statement_span(statement: &'a Statement<'tcx>) -> Option<
 
 /// If the MIR `Terminator` has a span contributive to computing coverage spans,
 /// return it; otherwise return `None`.
-pub(super) fn filtered_terminator_span(terminator: &'a Terminator<'tcx>) -> Option<Span> {
+pub(super) fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
     match terminator.kind {
         // These terminators have spans that don't positively contribute to computing a reasonable
         // span of actually executed source code. (For example, SwitchInt terminators extracted from
index 14dd0a8b9245bdd83f121f616d6b356443ed53ec..b9c79d4cf2db80f2984713bab2e63a7eadb6e09d 100644 (file)
@@ -180,7 +180,7 @@ fn to_body(self) -> Body<'tcx> {
     }
 }
 
-fn debug_basic_blocks(mir_body: &Body<'tcx>) -> String {
+fn debug_basic_blocks<'tcx>(mir_body: &Body<'tcx>) -> String {
     format!(
         "{:?}",
         mir_body
@@ -273,7 +273,7 @@ fn print_coverage_graphviz(
 }
 
 /// Create a mock `Body` with a simple flow.
-fn goto_switchint() -> Body<'a> {
+fn goto_switchint<'a>() -> Body<'a> {
     let mut blocks = MockBlocks::new();
     let start = blocks.call(None);
     let goto = blocks.goto(Some(start));
@@ -363,7 +363,7 @@ fn test_covgraph_goto_switchint() {
 }
 
 /// Create a mock `Body` with a loop.
-fn switchint_then_loop_else_return() -> Body<'a> {
+fn switchint_then_loop_else_return<'a>() -> Body<'a> {
     let mut blocks = MockBlocks::new();
     let start = blocks.call(None);
     let switchint = blocks.switchint(Some(start));
@@ -449,7 +449,7 @@ fn test_covgraph_switchint_then_loop_else_return() {
 }
 
 /// Create a mock `Body` with nested loops.
-fn switchint_loop_then_inner_loop_else_break() -> Body<'a> {
+fn switchint_loop_then_inner_loop_else_break<'a>() -> Body<'a> {
     let mut blocks = MockBlocks::new();
     let start = blocks.call(None);
     let switchint = blocks.switchint(Some(start));
index 993c8eef711ce66224308c807c259abb2a5930c9..d1977ed49fe15df8de83d198ae05b8fadc918464 100644 (file)
@@ -54,7 +54,7 @@ fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Loca
     }
 }
 
-fn find_duplicates<'a, 'tcx>(body: &'a Body<'tcx>) -> FxHashMap<BasicBlock, BasicBlock> {
+fn find_duplicates(body: &Body<'_>) -> FxHashMap<BasicBlock, BasicBlock> {
     let mut duplicates = FxHashMap::default();
 
     let bbs_to_go_through =
@@ -102,7 +102,7 @@ struct BasicBlockHashable<'tcx, 'a> {
     basic_block_data: &'a BasicBlockData<'tcx>,
 }
 
-impl<'tcx, 'a> Hash for BasicBlockHashable<'tcx, 'a> {
+impl Hash for BasicBlockHashable<'_, '_> {
     fn hash<H: Hasher>(&self, state: &mut H) {
         hash_statements(state, self.basic_block_data.statements.iter());
         // Note that since we only hash the kind, we lose span information if we deduplicate the blocks
@@ -110,9 +110,9 @@ fn hash<H: Hasher>(&self, state: &mut H) {
     }
 }
 
-impl<'tcx, 'a> Eq for BasicBlockHashable<'tcx, 'a> {}
+impl Eq for BasicBlockHashable<'_, '_> {}
 
-impl<'tcx, 'a> PartialEq for BasicBlockHashable<'tcx, 'a> {
+impl PartialEq for BasicBlockHashable<'_, '_> {
     fn eq(&self, other: &Self) -> bool {
         self.basic_block_data.statements.len() == other.basic_block_data.statements.len()
             && &self.basic_block_data.terminator().kind == &other.basic_block_data.terminator().kind
@@ -132,7 +132,7 @@ fn hash_statements<'a, 'tcx, H: Hasher>(
     }
 }
 
-fn statement_hash<'tcx, H: Hasher>(hasher: &mut H, stmt: &StatementKind<'tcx>) {
+fn statement_hash<H: Hasher>(hasher: &mut H, stmt: &StatementKind<'_>) {
     match stmt {
         StatementKind::Assign(box (place, rvalue)) => {
             place.hash(hasher);
@@ -142,14 +142,14 @@ fn statement_hash<'tcx, H: Hasher>(hasher: &mut H, stmt: &StatementKind<'tcx>) {
     };
 }
 
-fn rvalue_hash<H: Hasher>(hasher: &mut H, rvalue: &Rvalue<'tcx>) {
+fn rvalue_hash<H: Hasher>(hasher: &mut H, rvalue: &Rvalue<'_>) {
     match rvalue {
         Rvalue::Use(op) => operand_hash(hasher, op),
         x => x.hash(hasher),
     };
 }
 
-fn operand_hash<H: Hasher>(hasher: &mut H, operand: &Operand<'tcx>) {
+fn operand_hash<H: Hasher>(hasher: &mut H, operand: &Operand<'_>) {
     match operand {
         Operand::Constant(box Constant { user_ty: _, literal, span: _ }) => literal.hash(hasher),
         x => x.hash(hasher),
@@ -168,7 +168,7 @@ fn statement_eq<'tcx>(lhs: &StatementKind<'tcx>, rhs: &StatementKind<'tcx>) -> b
     res
 }
 
-fn rvalue_eq(lhs: &Rvalue<'tcx>, rhs: &Rvalue<'tcx>) -> bool {
+fn rvalue_eq<'tcx>(lhs: &Rvalue<'tcx>, rhs: &Rvalue<'tcx>) -> bool {
     let res = match (lhs, rhs) {
         (Rvalue::Use(op1), Rvalue::Use(op2)) => operand_eq(op1, op2),
         (x, y) => x == y,
@@ -177,7 +177,7 @@ fn rvalue_eq(lhs: &Rvalue<'tcx>, rhs: &Rvalue<'tcx>) -> bool {
     res
 }
 
-fn operand_eq(lhs: &Operand<'tcx>, rhs: &Operand<'tcx>) -> bool {
+fn operand_eq<'tcx>(lhs: &Operand<'tcx>, rhs: &Operand<'tcx>) -> bool {
     let res = match (lhs, rhs) {
         (
             Operand::Constant(box Constant { user_ty: _, literal, span: _ }),
index 256f7fbd7592351b15777e411eed4ec2273ff7ec..2b382468be0f5d01c00fa61a740b60683063947a 100644 (file)
@@ -241,7 +241,7 @@ struct Replacements<'tcx> {
     kill: BitSet<Local>,
 }
 
-impl Replacements<'tcx> {
+impl<'tcx> Replacements<'tcx> {
     fn new(locals: usize) -> Self {
         Self { map: IndexVec::from_elem_n(None, locals), kill: BitSet::new_empty(locals) }
     }
@@ -298,7 +298,7 @@ struct Replacer<'tcx> {
 }
 
 impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
-    fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
 
@@ -372,7 +372,7 @@ struct Conflicts<'a> {
     unified_locals: InPlaceUnificationTable<UnifyLocal>,
 }
 
-impl Conflicts<'a> {
+impl<'a> Conflicts<'a> {
     fn build<'tcx>(
         tcx: TyCtxt<'tcx>,
         body: &'_ Body<'tcx>,
@@ -820,10 +820,7 @@ struct CandidateAssignment<'tcx> {
 /// comment) and also throw out assignments that involve a local that has its address taken or is
 /// otherwise ineligible (eg. locals used as array indices are ignored because we cannot propagate
 /// arbitrary places into array indices).
-fn find_candidates<'a, 'tcx>(
-    tcx: TyCtxt<'tcx>,
-    body: &'a Body<'tcx>,
-) -> Vec<CandidateAssignment<'tcx>> {
+fn find_candidates<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> Vec<CandidateAssignment<'tcx>> {
     let mut visitor = FindAssignments {
         tcx,
         body,
@@ -843,7 +840,7 @@ struct FindAssignments<'a, 'tcx> {
     locals_used_as_array_index: BitSet<Local>,
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for FindAssignments<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> {
     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
         if let StatementKind::Assign(box (
             dest,
index 62e82aca262073cb99b17bda75865d42569d6ab7..ac88060f0d349de5d45afa87e5a7678dc984da5f 100644 (file)
@@ -167,7 +167,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     }
 }
 
-fn is_switch<'tcx>(terminator: &Terminator<'tcx>) -> bool {
+fn is_switch(terminator: &Terminator<'_>) -> bool {
     matches!(terminator.kind, TerminatorKind::SwitchInt { .. })
 }
 
@@ -208,7 +208,7 @@ struct OptimizationInfo<'tcx> {
     second_switch_info: SwitchDiscriminantInfo<'tcx>,
 }
 
-impl<'a, 'tcx> Helper<'a, 'tcx> {
+impl<'tcx> Helper<'_, 'tcx> {
     pub fn go(
         &self,
         bb: &BasicBlockData<'tcx>,
index af13c734e5b9a399720552ffd3fcc4e88f675403..d346dfb1772199b3659a3962a7355fb056e357cb 100644 (file)
@@ -149,13 +149,13 @@ struct Elaborator<'a, 'b, 'tcx> {
     ctxt: &'a mut ElaborateDropsCtxt<'b, 'tcx>,
 }
 
-impl<'a, 'b, 'tcx> fmt::Debug for Elaborator<'a, 'b, 'tcx> {
+impl fmt::Debug for Elaborator<'_, '_, '_> {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
         Ok(())
     }
 }
 
-impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> {
+impl<'a, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, '_, 'tcx> {
     type Path = MovePathIndex;
 
     fn patch(&mut self) -> &mut MirPatch<'tcx> {
index c05dc39afc47e329cae2c583bd19baadba8d8eaa..05834b443d0d9d0552d4b2e78fd6a6dabde1c7ff 100644 (file)
@@ -27,7 +27,7 @@ struct FunctionItemRefChecker<'a, 'tcx> {
     body: &'a Body<'tcx>,
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for FunctionItemRefChecker<'_, 'tcx> {
     /// Emits a lint for function reference arguments bound by `fmt::Pointer` or passed to
     /// `transmute`. This only handles arguments in calls outside macro expansions to avoid double
     /// counting function references formatted as pointers by macros.
@@ -92,7 +92,7 @@ fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
     }
 }
 
-impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> {
+impl<'tcx> FunctionItemRefChecker<'_, 'tcx> {
     /// Emits a lint for function reference arguments bound by `fmt::Pointer` in calls to the
     /// function defined by `def_id` with the substitutions `substs_ref`.
     fn check_bound_args(
index 5376855035e71d3f1f35bd78ba0dfd2edac39e1a..6220cee8d21627170426bfc7fbdfd4ffffae4e06 100644 (file)
@@ -233,7 +233,7 @@ struct TransformVisitor<'tcx> {
     new_ret_local: Local,
 }
 
-impl TransformVisitor<'tcx> {
+impl<'tcx> TransformVisitor<'tcx> {
     // Make a GeneratorState variant assignment. `core::ops::GeneratorState` only has single
     // element tuple variants, so we can just write to the downcasted first field and then set the
     // discriminant to the appropriate variant.
@@ -295,7 +295,7 @@ fn get_discr(&self, body: &mut Body<'tcx>) -> (Statement<'tcx>, Place<'tcx>) {
     }
 }
 
-impl MutVisitor<'tcx> for TransformVisitor<'tcx> {
+impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
@@ -446,7 +446,7 @@ struct LivenessInfo {
     storage_liveness: IndexVec<BasicBlock, Option<BitSet<Local>>>,
 }
 
-fn locals_live_across_suspend_points(
+fn locals_live_across_suspend_points<'tcx>(
     tcx: TyCtxt<'tcx>,
     body: &Body<'tcx>,
     always_live_locals: &storage::AlwaysLiveLocals,
@@ -613,7 +613,7 @@ fn deref(&self) -> &Self::Target {
 /// time. Generates a bitset for every local of all the other locals that may be
 /// StorageLive simultaneously with that local. This is used in the layout
 /// computation; see `GeneratorLayout` for more.
-fn compute_storage_conflicts(
+fn compute_storage_conflicts<'mir, 'tcx>(
     body: &'mir Body<'tcx>,
     saved_locals: &GeneratorSavedLocals,
     always_live_locals: storage::AlwaysLiveLocals,
@@ -672,7 +672,9 @@ struct StorageConflictVisitor<'mir, 'tcx, 's> {
     local_conflicts: BitMatrix<Local, Local>,
 }
 
-impl rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx> for StorageConflictVisitor<'mir, 'tcx, '_> {
+impl<'mir, 'tcx> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx>
+    for StorageConflictVisitor<'mir, 'tcx, '_>
+{
     type FlowState = BitSet<Local>;
 
     fn visit_statement_before_primary_effect(
@@ -694,7 +696,7 @@ fn visit_terminator_before_primary_effect(
     }
 }
 
-impl<'body, 'tcx, 's> StorageConflictVisitor<'body, 'tcx, 's> {
+impl StorageConflictVisitor<'_, '_, '_> {
     fn apply_state(&mut self, flow_state: &BitSet<Local>, loc: Location) {
         // Ignore unreachable blocks.
         if self.body.basic_blocks()[loc.block].terminator().kind == TerminatorKind::Unreachable {
@@ -1398,7 +1400,7 @@ fn saved_local_for_direct_place(&self, place: Place<'_>) -> Option<GeneratorSave
         self.saved_locals.get(place.local)
     }
 
-    fn check_assigned_place(&mut self, place: Place<'tcx>, f: impl FnOnce(&mut Self)) {
+    fn check_assigned_place(&mut self, place: Place<'_>, f: impl FnOnce(&mut Self)) {
         if let Some(assigned_local) = self.saved_local_for_direct_place(place) {
             assert!(self.assigned_local.is_none(), "`check_assigned_place` must not recurse");
 
@@ -1409,7 +1411,7 @@ fn check_assigned_place(&mut self, place: Place<'tcx>, f: impl FnOnce(&mut Self)
     }
 }
 
-impl Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
+impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
     fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
         let lhs = match self.assigned_local {
             Some(l) => l,
index 81454cc4070bdf3649d321042ca9bf267a8d5b2c..558b1ce082e40ca25151d7c7d40d4e31cde5050b 100644 (file)
@@ -57,7 +57,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     }
 }
 
-fn inline(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
+fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
     let def_id = body.source.def_id();
     let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
 
@@ -95,7 +95,7 @@ struct Inliner<'tcx> {
     changed: bool,
 }
 
-impl Inliner<'tcx> {
+impl<'tcx> Inliner<'tcx> {
     fn process_blocks(&mut self, caller_body: &mut Body<'tcx>, blocks: Range<BasicBlock>) {
         for bb in blocks {
             let bb_data = &caller_body[bb];
@@ -786,7 +786,7 @@ struct Integrator<'a, 'tcx> {
     always_live_locals: BitSet<Local>,
 }
 
-impl<'a, 'tcx> Integrator<'a, 'tcx> {
+impl Integrator<'_, '_> {
     fn map_local(&self, local: Local) -> Local {
         let new = if local == RETURN_PLACE {
             self.destination.local
@@ -815,7 +815,7 @@ fn map_block(&self, block: BasicBlock) -> BasicBlock {
     }
 }
 
-impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
+impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
index 385394ba67d4d2e8fa0fdc2f8747c59129817d11..747e760a18b92e8a88f6717614288d495e260612 100644 (file)
@@ -10,7 +10,7 @@
 // FIXME: check whether it is cheaper to precompute the entire call graph instead of invoking
 // this query riddiculously often.
 #[instrument(level = "debug", skip(tcx, root, target))]
-crate fn mir_callgraph_reachable(
+crate fn mir_callgraph_reachable<'tcx>(
     tcx: TyCtxt<'tcx>,
     (root, target): (ty::Instance<'tcx>, LocalDefId),
 ) -> bool {
@@ -33,7 +33,7 @@
         level = "debug",
         skip(tcx, param_env, target, stack, seen, recursion_limiter, caller, recursion_limit)
     )]
-    fn process(
+    fn process<'tcx>(
         tcx: TyCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         caller: ty::Instance<'tcx>,
index c5adc241664d246ff17831f073bd8e7432927bee..792ac68671efb1f47c8ce0bad49d8d9b8c7c15f7 100644 (file)
@@ -38,7 +38,7 @@ struct InstCombineContext<'tcx, 'a> {
     local_decls: &'a LocalDecls<'tcx>,
 }
 
-impl<'tcx, 'a> InstCombineContext<'tcx, 'a> {
+impl<'tcx> InstCombineContext<'tcx, '_> {
     fn should_combine(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool {
         self.tcx.consider_optimizing(|| {
             format!("InstCombine - Rvalue: {:?} SourceInfo: {:?}", rvalue, source_info)
index a7e003a55b4007e3fe4f901aa1be8dc9b57fd878..0e7488aa98eee5eac87603a03d1183a6683d23d2 100644 (file)
@@ -1,7 +1,6 @@
 #![feature(box_patterns)]
 #![feature(box_syntax)]
 #![feature(crate_visibility_modifier)]
-#![feature(in_band_lifetimes)]
 #![feature(iter_zip)]
 #![feature(let_else)]
 #![feature(map_try_insert)]
@@ -150,7 +149,7 @@ struct GatherCtors<'a, 'tcx> {
         tcx: TyCtxt<'tcx>,
         set: &'a mut FxHashSet<LocalDefId>,
     }
-    impl<'a, 'tcx> Visitor<'tcx> for GatherCtors<'a, 'tcx> {
+    impl<'tcx> Visitor<'tcx> for GatherCtors<'_, 'tcx> {
         fn visit_variant_data(
             &mut self,
             v: &'tcx hir::VariantData<'tcx>,
@@ -243,7 +242,7 @@ fn mir_const<'tcx>(
 }
 
 /// Compute the main MIR body and the list of MIR bodies of the promoteds.
-fn mir_promoted(
+fn mir_promoted<'tcx>(
     tcx: TyCtxt<'tcx>,
     def: ty::WithOptConstParam<LocalDefId>,
 ) -> (&'tcx Steal<Body<'tcx>>, &'tcx Steal<IndexVec<Promoted, Body<'tcx>>>) {
@@ -480,8 +479,7 @@ fn o1<T>(x: T) -> WithMinOptLevel<T> {
             // FIXME(#70073): This pass is responsible for both optimization as well as some lints.
             &const_prop::ConstProp,
             //
-            // FIXME: The old pass manager ran this only at mir-opt-level >= 1, but
-            // const-prop runs unconditionally. Should this run unconditionally as well?
+            // Const-prop runs unconditionally, but doesn't mutate the MIR at mir-opt-level=0.
             &o1(simplify_branches::SimplifyConstCondition::new("after-const-prop")),
             &early_otherwise_branch::EarlyOtherwiseBranch,
             &simplify_comparison_integral::SimplifyComparisonIntegral,
index 5848163af72fc22b0cc2d09f56f5f308e445852d..4c4497ad629da7c2d4428f0f5223e57863bba5ed 100644 (file)
@@ -135,7 +135,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     }
 }
 
-fn resolve_rust_intrinsic(
+fn resolve_rust_intrinsic<'tcx>(
     tcx: TyCtxt<'tcx>,
     func_ty: Ty<'tcx>,
 ) -> Option<(Symbol, SubstsRef<'tcx>)> {
@@ -148,7 +148,7 @@ fn resolve_rust_intrinsic(
     None
 }
 
-fn validate_simd_shuffle(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
+fn validate_simd_shuffle<'tcx>(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
     match &args[2] {
         Operand::Constant(_) => {} // all good
         _ => {
index 0fd9e0352a24978a6a39d9abb3c1dedb62a00d35..e4ac57ac92508bd6d626321541823b127c32f7d2 100644 (file)
@@ -85,7 +85,7 @@ struct Patcher<'a, 'tcx> {
     statement_idx: usize,
 }
 
-impl<'a, 'tcx> Patcher<'a, 'tcx> {
+impl<'tcx> Patcher<'_, 'tcx> {
     fn patch_expand_statement(
         &mut self,
         statement: &mut Statement<'tcx>,
index 88ec34b73ec401a833e4bb5a9777492731130c8f..797f7ee2685b83ed424d0d87d39b1a6fdd9b6819 100644 (file)
@@ -165,7 +165,7 @@ struct RenameToReturnPlace<'tcx> {
 }
 
 /// Replaces all uses of `self.to_rename` with `_0`.
-impl MutVisitor<'tcx> for RenameToReturnPlace<'tcx> {
+impl<'tcx> MutVisitor<'tcx> for RenameToReturnPlace<'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
@@ -221,7 +221,7 @@ fn run(body: &mir::Body<'_>) -> bool {
     }
 }
 
-impl Visitor<'tcx> for IsReturnPlaceRead {
+impl<'tcx> Visitor<'tcx> for IsReturnPlaceRead {
     fn visit_local(&mut self, &l: &Local, ctxt: PlaceContext, _: Location) {
         if l == mir::RETURN_PLACE && ctxt.is_use() && !ctxt.is_place_assignment() {
             self.0 = true;
index 729b8cae5e47acad54078a4e9b303577960c44ad..8725eae870917856985ed83b281d416351183b9c 100644 (file)
@@ -28,7 +28,7 @@ fn is_enabled(&self, _sess: &Session) -> bool {
 #[derive(Debug, Clone)]
 pub struct Lint<T>(pub T);
 
-impl<T> MirPass<'tcx> for Lint<T>
+impl<'tcx, T> MirPass<'tcx> for Lint<T>
 where
     T: MirLint<'tcx>,
 {
@@ -51,7 +51,7 @@ fn is_mir_dump_enabled(&self) -> bool {
 
 pub struct WithMinOptLevel<T>(pub u32, pub T);
 
-impl<T> MirPass<'tcx> for WithMinOptLevel<T>
+impl<'tcx, T> MirPass<'tcx> for WithMinOptLevel<T>
 where
     T: MirPass<'tcx>,
 {
@@ -72,7 +72,7 @@ fn phase_change(&self) -> Option<MirPhase> {
     }
 }
 
-pub fn run_passes(tcx: TyCtxt<'tcx>, body: &'mir mut Body<'tcx>, passes: &[&dyn MirPass<'tcx>]) {
+pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn MirPass<'tcx>]) {
     let start_phase = body.phase;
     let mut cnt = 0;
 
@@ -119,11 +119,11 @@ pub fn run_passes(tcx: TyCtxt<'tcx>, body: &'mir mut Body<'tcx>, passes: &[&dyn
     }
 }
 
-pub fn validate_body(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when: String) {
+pub fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when: String) {
     validate::Validator { when, mir_phase: body.phase }.run_pass(tcx, body);
 }
 
-pub fn dump_mir(
+pub fn dump_mir<'tcx>(
     tcx: TyCtxt<'tcx>,
     body: &Body<'tcx>,
     phase: MirPhase,
index c219f2673244144721a172cd4486de71716457a4..fc5ac97e3e1dc47485b257909ac22752702b58d7 100644 (file)
@@ -86,7 +86,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     }
 }
 
-fn is_needs_drop_and_init(
+fn is_needs_drop_and_init<'tcx>(
     tcx: TyCtxt<'tcx>,
     param_env: ParamEnv<'tcx>,
     maybe_inits: &BitSet<MovePathIndex>,
@@ -158,7 +158,7 @@ fn is_needs_drop_and_init(
     }
 }
 
-fn variant_needs_drop(
+fn variant_needs_drop<'tcx>(
     tcx: TyCtxt<'tcx>,
     param_env: ParamEnv<'tcx>,
     substs: SubstsRef<'tcx>,
index 8b64ad65ab35ca5ac000eb3c983ce0ea71d14adf..80c87cafea1016afe42477322df728e3dba090be 100644 (file)
@@ -12,7 +12,7 @@ pub fn new(required_consts: &'a mut Vec<Constant<'tcx>>) -> Self {
     }
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for RequiredConstsVisitor<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {
     fn visit_constant(&mut self, constant: &Constant<'tcx>, _: Location) {
         if let Some(ct) = constant.literal.const_for_ty() {
             if let ConstKind::Unevaluated(_) = ct.val {
index 7450d53ba717ee3ee3cb6d025b51e88cc51d374a..612fce71f9167a1bf88df39dc130c0fdf6ceaf30 100644 (file)
@@ -59,7 +59,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
 }
 
 /// Returns the amount of blocks that were duplicated
-pub fn separate_const_switch<'tcx>(body: &mut Body<'tcx>) -> usize {
+pub fn separate_const_switch(body: &mut Body<'_>) -> usize {
     let mut new_blocks: SmallVec<[(BasicBlock, BasicBlock); 6]> = SmallVec::new();
     let predecessors = body.predecessors();
     'block_iter: for (block_id, block) in body.basic_blocks().iter_enumerated() {
index 193a9e6ad291fe98adb4785d26fd7e5cb8488811..d0039380361444119504ce6cecd8601cb196a797 100644 (file)
@@ -64,7 +64,19 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
 
             build_call_shim(tcx, instance, Some(Adjustment::RefMut), CallKind::Direct(call_mut))
         }
-        ty::InstanceDef::DropGlue(def_id, ty) => build_drop_shim(tcx, def_id, ty),
+
+        ty::InstanceDef::DropGlue(def_id, ty) => {
+            // FIXME(#91576): Drop shims for generators aren't subject to the MIR passes at the end
+            // of this function. Is this intentional?
+            if let Some(ty::Generator(gen_def_id, substs, _)) = ty.map(ty::TyS::kind) {
+                let body = tcx.optimized_mir(*gen_def_id).generator_drop().unwrap();
+                let body = body.clone().subst(tcx, substs);
+                debug!("make_shim({:?}) = {:?}", instance, body);
+                return body;
+            }
+
+            build_drop_shim(tcx, def_id, ty)
+        }
         ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty),
         ty::InstanceDef::Virtual(..) => {
             bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance)
@@ -75,14 +87,6 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
     };
     debug!("make_shim({:?}) = untransformed {:?}", instance, result);
 
-    // In some of the above cases, we seem to be invoking the passes for non-shim MIR bodies.
-    // If that happens, there's no need to run them again.
-    //
-    // FIXME: Is this intentional?
-    if result.phase >= MirPhase::Const {
-        return result;
-    }
-
     pm::run_passes(
         tcx,
         &mut result,
@@ -140,11 +144,7 @@ fn local_decls_for_sig<'tcx>(
 fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) -> Body<'tcx> {
     debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
 
-    // Check if this is a generator, if so, return the drop glue for it
-    if let Some(&ty::Generator(gen_def_id, substs, _)) = ty.map(|ty| ty.kind()) {
-        let body = tcx.optimized_mir(gen_def_id).generator_drop().unwrap();
-        return body.clone().subst(tcx, substs);
-    }
+    assert!(!matches!(ty, Some(ty) if ty.is_generator()));
 
     let substs = if let Some(ty) = ty {
         tcx.intern_substs(&[ty.into()])
@@ -247,7 +247,7 @@ pub struct DropShimElaborator<'a, 'tcx> {
     pub param_env: ty::ParamEnv<'tcx>,
 }
 
-impl<'a, 'tcx> fmt::Debug for DropShimElaborator<'a, 'tcx> {
+impl fmt::Debug for DropShimElaborator<'_, '_> {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
         Ok(())
     }
@@ -337,7 +337,7 @@ struct CloneShimBuilder<'tcx> {
     sig: ty::FnSig<'tcx>,
 }
 
-impl CloneShimBuilder<'tcx> {
+impl<'tcx> CloneShimBuilder<'tcx> {
     fn new(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Self {
         // we must subst the self_ty because it's
         // otherwise going to be TySelf and we can't index
index 677869a0bdb6ad4add198e258b318c12d1026cc1..7992124bacd43d924c7252578a435bf510d94bac 100644 (file)
@@ -47,7 +47,7 @@ pub fn new(label: &str) -> Self {
     }
 }
 
-pub fn simplify_cfg(tcx: TyCtxt<'tcx>, body: &mut Body<'_>) {
+pub fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     CfgSimplifier::new(body).simplify();
     remove_dead_blocks(tcx, body);
 
@@ -262,7 +262,7 @@ fn strip_nops(&mut self) {
     }
 }
 
-pub fn remove_dead_blocks(tcx: TyCtxt<'tcx>, body: &mut Body<'_>) {
+pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     let reachable = traversal::reachable_as_bitset(body);
     let num_blocks = body.basic_blocks().len();
     if num_blocks == reachable.count() {
@@ -454,7 +454,7 @@ fn is_used(&self, local: Local) -> bool {
     }
 
     /// Updates the use counts to reflect the removal of given statement.
-    fn statement_removed(&mut self, statement: &Statement<'tcx>) {
+    fn statement_removed(&mut self, statement: &Statement<'_>) {
         self.increment = false;
 
         // The location of the statement is irrelevant.
@@ -463,7 +463,7 @@ fn statement_removed(&mut self, statement: &Statement<'tcx>) {
     }
 
     /// Visits a left-hand side of an assignment.
-    fn visit_lhs(&mut self, place: &Place<'tcx>, location: Location) {
+    fn visit_lhs(&mut self, place: &Place<'_>, location: Location) {
         if place.is_indirect() {
             // A use, not a definition.
             self.visit_place(place, PlaceContext::MutatingUse(MutatingUseContext::Store), location);
@@ -480,7 +480,7 @@ fn visit_lhs(&mut self, place: &Place<'tcx>, location: Location) {
     }
 }
 
-impl Visitor<'_> for UsedLocals {
+impl<'tcx> Visitor<'tcx> for UsedLocals {
     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
         match statement.kind {
             StatementKind::LlvmInlineAsm(..)
@@ -518,7 +518,7 @@ fn visit_local(&mut self, local: &Local, _ctx: PlaceContext, _location: Location
 }
 
 /// Removes unused definitions. Updates the used locals to reflect the changes made.
-fn remove_unused_definitions<'a, 'tcx>(used_locals: &'a mut UsedLocals, body: &mut Body<'tcx>) {
+fn remove_unused_definitions(used_locals: &mut UsedLocals, body: &mut Body<'_>) {
     // The use counts are updated as we remove the statements. A local might become unused
     // during the retain operation, leading to a temporary inconsistency (storage statements or
     // definitions referencing the local might remain). For correctness it is crucial that this
index 4b261334f3e545d7d4718de15b106861abed1c24..3bbae5b8976ccde862a084291b64948a56de8315 100644 (file)
@@ -33,15 +33,8 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
                 } => {
                     let constant = c.literal.try_eval_bits(tcx, param_env, switch_ty);
                     if let Some(constant) = constant {
-                        let otherwise = targets.otherwise();
-                        let mut ret = TerminatorKind::Goto { target: otherwise };
-                        for (v, t) in targets.iter() {
-                            if v == constant {
-                                ret = TerminatorKind::Goto { target: t };
-                                break;
-                            }
-                        }
-                        ret
+                        let target = targets.target_for_value(constant);
+                        TerminatorKind::Goto { target }
                     } else {
                         continue;
                     }
index 3bd68e8210d5b1663f925c24d70cd17208a2777a..da683a33651d69c4e5f41cf3961d68398da5c3b5 100644 (file)
@@ -148,7 +148,7 @@ struct OptimizationFinder<'a, 'tcx> {
     body: &'a Body<'tcx>,
 }
 
-impl<'a, 'tcx> OptimizationFinder<'a, 'tcx> {
+impl<'tcx> OptimizationFinder<'_, 'tcx> {
     fn find_optimizations(&self) -> Vec<OptimizationInfo<'tcx>> {
         self.body
             .basic_blocks()
index e436d73226a5515d12b2ffabc69868c53153963f..7761d4006d3dbd06f4cea316e70ef823360603e9 100644 (file)
@@ -102,7 +102,7 @@ fn get_arm_identity_info<'a, 'tcx>(
 
     type StmtIter<'a, 'tcx> = Peekable<Enumerate<Iter<'a, Statement<'tcx>>>>;
 
-    fn is_storage_stmt<'tcx>(stmt: &Statement<'tcx>) -> bool {
+    fn is_storage_stmt(stmt: &Statement<'_>) -> bool {
         matches!(stmt.kind, StatementKind::StorageLive(_) | StatementKind::StorageDead(_))
     }
 
@@ -122,8 +122,8 @@ fn try_eat<'a, 'tcx>(
 
     /// Eats consecutive `StorageLive` and `StorageDead` Statements.
     /// The iterator `stmt_iter` is not advanced if none were found.
-    fn try_eat_storage_stmts<'a, 'tcx>(
-        stmt_iter: &mut StmtIter<'a, 'tcx>,
+    fn try_eat_storage_stmts(
+        stmt_iter: &mut StmtIter<'_, '_>,
         storage_live_stmts: &mut Vec<(usize, Local)>,
         storage_dead_stmts: &mut Vec<(usize, Local)>,
     ) {
@@ -136,7 +136,7 @@ fn try_eat_storage_stmts<'a, 'tcx>(
         })
     }
 
-    fn is_tmp_storage_stmt<'tcx>(stmt: &Statement<'tcx>) -> bool {
+    fn is_tmp_storage_stmt(stmt: &Statement<'_>) -> bool {
         use rustc_middle::mir::StatementKind::Assign;
         if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) = &stmt.kind {
             place.as_local().is_some() && p.as_local().is_some()
@@ -147,8 +147,8 @@ fn is_tmp_storage_stmt<'tcx>(stmt: &Statement<'tcx>) -> bool {
 
     /// Eats consecutive `Assign` Statements.
     // The iterator `stmt_iter` is not advanced if none were found.
-    fn try_eat_assign_tmp_stmts<'a, 'tcx>(
-        stmt_iter: &mut StmtIter<'a, 'tcx>,
+    fn try_eat_assign_tmp_stmts(
+        stmt_iter: &mut StmtIter<'_, '_>,
         tmp_assigns: &mut Vec<(Local, Local)>,
         nop_stmts: &mut Vec<usize>,
     ) {
@@ -163,9 +163,9 @@ fn try_eat_assign_tmp_stmts<'a, 'tcx>(
         })
     }
 
-    fn find_storage_live_dead_stmts_for_local<'tcx>(
+    fn find_storage_live_dead_stmts_for_local(
         local: Local,
-        stmts: &[Statement<'tcx>],
+        stmts: &[Statement<'_>],
     ) -> Option<(usize, usize)> {
         trace!("looking for {:?}", local);
         let mut storage_live_stmt = None;
@@ -452,14 +452,14 @@ struct LocalUseCounter {
 }
 
 impl LocalUseCounter {
-    fn get_local_uses<'tcx>(body: &Body<'tcx>) -> IndexVec<Local, usize> {
+    fn get_local_uses(body: &Body<'_>) -> IndexVec<Local, usize> {
         let mut counter = LocalUseCounter { local_uses: IndexVec::from_elem(0, &body.local_decls) };
         counter.visit_body(body);
         counter.local_uses
     }
 }
 
-impl<'tcx> Visitor<'tcx> for LocalUseCounter {
+impl Visitor<'_> for LocalUseCounter {
     fn visit_local(&mut self, local: &Local, context: PlaceContext, _location: Location) {
         if context.is_storage_marker()
             || context == PlaceContext::NonUse(NonUseContext::VarDebugInfo)
@@ -510,7 +510,7 @@ fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local
 /// ```rust
 /// discriminant(_LOCAL_TO_SET) = VAR_IDX;
 /// ```
-fn match_set_discr<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, VariantIdx)> {
+fn match_set_discr(stmt: &Statement<'_>) -> Option<(Local, VariantIdx)> {
     match &stmt.kind {
         StatementKind::SetDiscriminant { place, variant_index } => {
             Some((place.as_local()?, *variant_index))
@@ -588,7 +588,7 @@ struct SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
 }
 
-impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
+impl<'tcx> SimplifyBranchSameOptimizationFinder<'_, 'tcx> {
     fn find(&self) -> Vec<SimplifyBranchSameOptimization> {
         self.body
             .basic_blocks()
index 37071ba611708dda99ccd2d9b09ed72ebeb5abe0..9e755ab141a489e5860075e21887648b93bce2a5 100644 (file)
@@ -64,7 +64,7 @@ fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     }
 }
 
-fn remove_successors<F>(
+fn remove_successors<'tcx, F>(
     terminator_kind: &TerminatorKind<'tcx>,
     predicate: F,
 ) -> Option<TerminatorKind<'tcx>>
index b402b8ba53ada3b3a0a96e3307e6185f5b2ccbf1..ca92d6b7fd04df4aa5a30b3b0541d7df1e4e1a7c 100644 (file)
@@ -1,4 +1,4 @@
-use super::{AttrWrapper, Capturing, ForceCollect, Parser, PathStyle};
+use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle};
 use rustc_ast as ast;
 use rustc_ast::attr;
 use rustc_ast::token::{self, Nonterminal};
@@ -177,7 +177,7 @@ fn annotate_following_item_if_applicable(
             AttrWrapper::empty(),
             true,
             false,
-            |_| true,
+            FnParseMode { req_name: |_| true, req_body: true },
             ForceCollect::No,
         ) {
             Ok(Some(item)) => {
index 55af2c9ddd32f1cb9a5455e443a0e76b364d530a..8e2e6eaee58ef298e324ce9f3e3b20acf0dfd2ed 100644 (file)
@@ -1129,7 +1129,8 @@ pub(super) fn maybe_recover_from_bad_qpath_stage_2<T: RecoverQPath>(
     }
 
     pub fn maybe_consume_incorrect_semicolon(&mut self, items: &[P<Item>]) -> bool {
-        if self.eat(&token::Semi) {
+        if self.token.kind == TokenKind::Semi {
+            self.bump();
             let mut err = self.struct_span_err(self.prev_token.span, "expected item, found `;`");
             err.span_suggestion_short(
                 self.prev_token.span,
index cbeaf675be4e5657863ce3199814dd24b9da8f6e..831c64e3faf037e3f9ccf8626f076f3b7e97e64e 100644 (file)
@@ -78,16 +78,17 @@ pub fn parse_mod(
 
 impl<'a> Parser<'a> {
     pub fn parse_item(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<P<Item>>> {
-        self.parse_item_(|_| true, force_collect).map(|i| i.map(P))
+        let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true };
+        self.parse_item_(fn_parse_mode, force_collect).map(|i| i.map(P))
     }
 
     fn parse_item_(
         &mut self,
-        req_name: ReqName,
+        fn_parse_mode: FnParseMode,
         force_collect: ForceCollect,
     ) -> PResult<'a, Option<Item>> {
         let attrs = self.parse_outer_attributes()?;
-        self.parse_item_common(attrs, true, false, req_name, force_collect)
+        self.parse_item_common(attrs, true, false, fn_parse_mode, force_collect)
     }
 
     pub(super) fn parse_item_common(
@@ -95,7 +96,7 @@ pub(super) fn parse_item_common(
         attrs: AttrWrapper,
         mac_allowed: bool,
         attrs_allowed: bool,
-        req_name: ReqName,
+        fn_parse_mode: FnParseMode,
         force_collect: ForceCollect,
     ) -> PResult<'a, Option<Item>> {
         // Don't use `maybe_whole` so that we have precise control
@@ -113,7 +114,8 @@ pub(super) fn parse_item_common(
         let mut unclosed_delims = vec![];
         let item =
             self.collect_tokens_trailing_token(attrs, force_collect, |this: &mut Self, attrs| {
-                let item = this.parse_item_common_(attrs, mac_allowed, attrs_allowed, req_name);
+                let item =
+                    this.parse_item_common_(attrs, mac_allowed, attrs_allowed, fn_parse_mode);
                 unclosed_delims.append(&mut this.unclosed_delims);
                 Ok((item?, TrailingToken::None))
             })?;
@@ -127,12 +129,13 @@ fn parse_item_common_(
         mut attrs: Vec<Attribute>,
         mac_allowed: bool,
         attrs_allowed: bool,
-        req_name: ReqName,
+        fn_parse_mode: FnParseMode,
     ) -> PResult<'a, Option<Item>> {
         let lo = self.token.span;
         let vis = self.parse_visibility(FollowedByType::No)?;
         let mut def = self.parse_defaultness();
-        let kind = self.parse_item_kind(&mut attrs, mac_allowed, lo, &vis, &mut def, req_name)?;
+        let kind =
+            self.parse_item_kind(&mut attrs, mac_allowed, lo, &vis, &mut def, fn_parse_mode)?;
         if let Some((ident, kind)) = kind {
             self.error_on_unconsumed_default(def, &kind);
             let span = lo.to(self.prev_token.span);
@@ -192,7 +195,7 @@ fn parse_item_kind(
         lo: Span,
         vis: &Visibility,
         def: &mut Defaultness,
-        req_name: ReqName,
+        fn_parse_mode: FnParseMode,
     ) -> PResult<'a, Option<ItemInfo>> {
         let def_final = def == &Defaultness::Final;
         let mut def = || mem::replace(def, Defaultness::Final);
@@ -219,7 +222,7 @@ fn parse_item_kind(
             (Ident::empty(), ItemKind::Use(tree))
         } else if self.check_fn_front_matter(def_final) {
             // FUNCTION ITEM
-            let (ident, sig, generics, body) = self.parse_fn(attrs, req_name, lo)?;
+            let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo)?;
             (ident, ItemKind::Fn(Box::new(Fn { defaultness: def(), sig, generics, body })))
         } else if self.eat_keyword(kw::Extern) {
             if self.eat_keyword(kw::Crate) {
@@ -733,23 +736,26 @@ pub fn parse_impl_item(
         &mut self,
         force_collect: ForceCollect,
     ) -> PResult<'a, Option<Option<P<AssocItem>>>> {
-        self.parse_assoc_item(|_| true, force_collect)
+        let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true };
+        self.parse_assoc_item(fn_parse_mode, force_collect)
     }
 
     pub fn parse_trait_item(
         &mut self,
         force_collect: ForceCollect,
     ) -> PResult<'a, Option<Option<P<AssocItem>>>> {
-        self.parse_assoc_item(|edition| edition >= Edition::Edition2018, force_collect)
+        let fn_parse_mode =
+            FnParseMode { req_name: |edition| edition >= Edition::Edition2018, req_body: false };
+        self.parse_assoc_item(fn_parse_mode, force_collect)
     }
 
     /// Parses associated items.
     fn parse_assoc_item(
         &mut self,
-        req_name: ReqName,
+        fn_parse_mode: FnParseMode,
         force_collect: ForceCollect,
     ) -> PResult<'a, Option<Option<P<AssocItem>>>> {
-        Ok(self.parse_item_(req_name, force_collect)?.map(
+        Ok(self.parse_item_(fn_parse_mode, force_collect)?.map(
             |Item { attrs, id, span, vis, ident, kind, tokens }| {
                 let kind = match AssocItemKind::try_from(kind) {
                     Ok(kind) => kind,
@@ -944,7 +950,8 @@ pub fn parse_foreign_item(
         &mut self,
         force_collect: ForceCollect,
     ) -> PResult<'a, Option<Option<P<ForeignItem>>>> {
-        Ok(self.parse_item_(|_| true, force_collect)?.map(
+        let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: false };
+        Ok(self.parse_item_(fn_parse_mode, force_collect)?.map(
             |Item { attrs, id, span, vis, ident, kind, tokens }| {
                 let kind = match ForeignItemKind::try_from(kind) {
                     Ok(kind) => kind,
@@ -1484,7 +1491,8 @@ fn parse_field_ident(&mut self, adt_ty: &str, lo: Span) -> PResult<'a, Ident> {
         if !is_raw && ident.is_reserved() {
             let err = if self.check_fn_front_matter(false) {
                 // We use `parse_fn` to get a span for the function
-                if let Err(mut db) = self.parse_fn(&mut Vec::new(), |_| true, lo) {
+                let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true };
+                if let Err(mut db) = self.parse_fn(&mut Vec::new(), fn_parse_mode, lo) {
                     db.delay_as_bug();
                 }
                 let mut err = self.struct_span_err(
@@ -1698,25 +1706,82 @@ fn recover_nested_adt_item(&mut self, keyword: Symbol) -> PResult<'a, bool> {
 /// The parsing configuration used to parse a parameter list (see `parse_fn_params`).
 ///
 /// The function decides if, per-parameter `p`, `p` must have a pattern or just a type.
+///
+/// This function pointer accepts an edition, because in edition 2015, trait declarations
+/// were allowed to omit parameter names. In 2018, they became required.
 type ReqName = fn(Edition) -> bool;
 
+/// Parsing configuration for functions.
+///
+/// The syntax of function items is slightly different within trait definitions,
+/// impl blocks, and modules. It is still parsed using the same code, just with
+/// different flags set, so that even when the input is wrong and produces a parse
+/// error, it still gets into the AST and the rest of the parser and
+/// type checker can run.
+#[derive(Clone, Copy)]
+pub(crate) struct FnParseMode {
+    /// A function pointer that decides if, per-parameter `p`, `p` must have a
+    /// pattern or just a type. This field affects parsing of the parameters list.
+    ///
+    /// ```text
+    /// fn foo(alef: A) -> X { X::new() }
+    ///        -----^^ affects parsing this part of the function signature
+    ///        |
+    ///        if req_name returns false, then this name is optional
+    ///
+    /// fn bar(A) -> X;
+    ///        ^
+    ///        |
+    ///        if req_name returns true, this is an error
+    /// ```
+    ///
+    /// Calling this function pointer should only return false if:
+    ///
+    ///   * The item is being parsed inside of a trait definition.
+    ///     Within an impl block or a module, it should always evaluate
+    ///     to true.
+    ///   * The span is from Edition 2015. In particular, you can get a
+    ///     2015 span inside a 2021 crate using macros.
+    pub req_name: ReqName,
+    /// If this flag is set to `true`, then plain, semicolon-terminated function
+    /// prototypes are not allowed here.
+    ///
+    /// ```text
+    /// fn foo(alef: A) -> X { X::new() }
+    ///                      ^^^^^^^^^^^^
+    ///                      |
+    ///                      this is always allowed
+    ///
+    /// fn bar(alef: A, bet: B) -> X;
+    ///                             ^
+    ///                             |
+    ///                             if req_body is set to true, this is an error
+    /// ```
+    ///
+    /// This field should only be set to false if the item is inside of a trait
+    /// definition or extern block. Within an impl block or a module, it should
+    /// always be set to true.
+    pub req_body: bool,
+}
+
 /// Parsing of functions and methods.
 impl<'a> Parser<'a> {
     /// Parse a function starting from the front matter (`const ...`) to the body `{ ... }` or `;`.
     fn parse_fn(
         &mut self,
         attrs: &mut Vec<Attribute>,
-        req_name: ReqName,
+        fn_parse_mode: FnParseMode,
         sig_lo: Span,
     ) -> PResult<'a, (Ident, FnSig, Generics, Option<P<Block>>)> {
         let header = self.parse_fn_front_matter()?; // `const ... fn`
         let ident = self.parse_ident()?; // `foo`
         let mut generics = self.parse_generics()?; // `<'a, T, ...>`
-        let decl = self.parse_fn_decl(req_name, AllowPlus::Yes, RecoverReturnSign::Yes)?; // `(p: u8, ...)`
+        let decl =
+            self.parse_fn_decl(fn_parse_mode.req_name, AllowPlus::Yes, RecoverReturnSign::Yes)?; // `(p: u8, ...)`
         generics.where_clause = self.parse_where_clause()?; // `where T: Ord`
 
         let mut sig_hi = self.prev_token.span;
-        let body = self.parse_fn_body(attrs, &ident, &mut sig_hi)?; // `;` or `{ ... }`.
+        let body = self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body)?; // `;` or `{ ... }`.
         let fn_sig_span = sig_lo.to(sig_hi);
         Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body))
     }
@@ -1729,9 +1794,17 @@ fn parse_fn_body(
         attrs: &mut Vec<Attribute>,
         ident: &Ident,
         sig_hi: &mut Span,
+        req_body: bool,
     ) -> PResult<'a, Option<P<Block>>> {
-        let (inner_attrs, body) = if self.eat(&token::Semi) {
+        let has_semi = if req_body {
+            self.token.kind == TokenKind::Semi
+        } else {
+            // Only include `;` in list of expected tokens if body is not required
+            self.check(&TokenKind::Semi)
+        };
+        let (inner_attrs, body) = if has_semi {
             // Include the trailing semicolon in the span of the signature
+            self.expect_semi()?;
             *sig_hi = self.prev_token.span;
             (Vec::new(), None)
         } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() {
@@ -1752,9 +1825,12 @@ fn parse_fn_body(
                 .emit();
             (Vec::new(), Some(self.mk_block_err(span)))
         } else {
-            if let Err(mut err) =
-                self.expected_one_of_not_found(&[], &[token::Semi, token::OpenDelim(token::Brace)])
-            {
+            let expected = if req_body {
+                &[token::OpenDelim(token::Brace)][..]
+            } else {
+                &[token::Semi, token::OpenDelim(token::Brace)]
+            };
+            if let Err(mut err) = self.expected_one_of_not_found(&[], &expected) {
                 if self.token.kind == token::CloseDelim(token::Brace) {
                     // The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in
                     // the AST for typechecking.
index 9212aaa87d1947fdce1deecfb60ac460b662485b..6d534bece463e7461402de3762afa9be2ea8df4a 100644 (file)
@@ -14,6 +14,7 @@
 pub use attr_wrapper::AttrWrapper;
 pub use diagnostics::AttemptLocalParseRecovery;
 use diagnostics::Error;
+pub(crate) use item::FnParseMode;
 pub use pat::{RecoverColon, RecoverComma};
 pub use path::PathStyle;
 
index 01e751ea8b5bff53bff164fd6a936c49e61f371e..d3e7d1690ccf6bcd8de52d420b97e9b1faf8e360 100644 (file)
@@ -4,7 +4,9 @@
 use super::pat::RecoverComma;
 use super::path::PathStyle;
 use super::TrailingToken;
-use super::{AttrWrapper, BlockMode, ForceCollect, Parser, Restrictions, SemiColonMode};
+use super::{
+    AttrWrapper, BlockMode, FnParseMode, ForceCollect, Parser, Restrictions, SemiColonMode,
+};
 use crate::maybe_whole;
 
 use rustc_ast as ast;
@@ -79,9 +81,13 @@ pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<
             } else {
                 self.parse_stmt_path_start(lo, attrs)
             }?
-        } else if let Some(item) =
-            self.parse_item_common(attrs.clone(), false, true, |_| true, force_collect)?
-        {
+        } else if let Some(item) = self.parse_item_common(
+            attrs.clone(),
+            false,
+            true,
+            FnParseMode { req_name: |_| true, req_body: true },
+            force_collect,
+        )? {
             // FIXME: Bad copy of attrs
             self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
         } else if self.eat(&token::Semi) {
index 6e36184aff051776552c22f1bfb8b4272261c271..cc1216418ae79306d54a5b46bb6b69a261afcdef 100644 (file)
@@ -55,6 +55,13 @@ macro_rules! write_leb128 {
     }};
 }
 
+/// A byte that [cannot occur in UTF8 sequences][utf8]. Used to mark the end of a string.
+/// This way we can skip validation and still be relatively sure that deserialization
+/// did not desynchronize.
+///
+/// [utf8]: https://en.wikipedia.org/w/index.php?title=UTF-8&oldid=1058865525#Codepage_layout
+const STR_SENTINEL: u8 = 0xC1;
+
 impl serialize::Encoder for Encoder {
     type Error = !;
 
@@ -150,7 +157,8 @@ fn emit_char(&mut self, v: char) -> EncodeResult {
     #[inline]
     fn emit_str(&mut self, v: &str) -> EncodeResult {
         self.emit_usize(v.len())?;
-        self.emit_raw_bytes(v.as_bytes())
+        self.emit_raw_bytes(v.as_bytes())?;
+        self.emit_u8(STR_SENTINEL)
     }
 
     #[inline]
@@ -502,7 +510,8 @@ fn emit_char(&mut self, v: char) -> FileEncodeResult {
     #[inline]
     fn emit_str(&mut self, v: &str) -> FileEncodeResult {
         self.emit_usize(v.len())?;
-        self.emit_raw_bytes(v.as_bytes())
+        self.emit_raw_bytes(v.as_bytes())?;
+        self.emit_u8(STR_SENTINEL)
     }
 
     #[inline]
@@ -656,8 +665,12 @@ fn read_char(&mut self) -> Result<char, Self::Error> {
     #[inline]
     fn read_str(&mut self) -> Result<Cow<'_, str>, Self::Error> {
         let len = self.read_usize()?;
-        let s = std::str::from_utf8(&self.data[self.position..self.position + len]).unwrap();
-        self.position += len;
+        let sentinel = self.data[self.position + len];
+        assert!(sentinel == STR_SENTINEL);
+        let s = unsafe {
+            std::str::from_utf8_unchecked(&self.data[self.position..self.position + len])
+        };
+        self.position += len + 1;
         Ok(Cow::Borrowed(s))
     }
 
index 16b68d95858b87b9c63ef82cc17def481aa69aed..87e8e57611765ab053b69a42c4e4f0339a1bc7eb 100644 (file)
@@ -746,6 +746,7 @@ fn default() -> Options {
             edition: DEFAULT_EDITION,
             json_artifact_notifications: false,
             json_unused_externs: false,
+            json_future_incompat: false,
             pretty: None,
             working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()),
         }
@@ -1257,6 +1258,7 @@ pub struct JsonConfig {
     pub json_rendered: HumanReadableErrorType,
     pub json_artifact_notifications: bool,
     pub json_unused_externs: bool,
+    pub json_future_incompat: bool,
 }
 
 /// Parse the `--json` flag.
@@ -1269,6 +1271,7 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
     let mut json_color = ColorConfig::Never;
     let mut json_artifact_notifications = false;
     let mut json_unused_externs = false;
+    let mut json_future_incompat = false;
     for option in matches.opt_strs("json") {
         // For now conservatively forbid `--color` with `--json` since `--json`
         // won't actually be emitting any colors and anything colorized is
@@ -1286,6 +1289,7 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
                 "diagnostic-rendered-ansi" => json_color = ColorConfig::Always,
                 "artifacts" => json_artifact_notifications = true,
                 "unused-externs" => json_unused_externs = true,
+                "future-incompat" => json_future_incompat = true,
                 s => early_error(
                     ErrorOutputType::default(),
                     &format!("unknown `--json` option `{}`", s),
@@ -1298,6 +1302,7 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
         json_rendered: json_rendered(json_color),
         json_artifact_notifications,
         json_unused_externs,
+        json_future_incompat,
     }
 }
 
@@ -2011,8 +2016,12 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     let edition = parse_crate_edition(matches);
 
-    let JsonConfig { json_rendered, json_artifact_notifications, json_unused_externs } =
-        parse_json(matches);
+    let JsonConfig {
+        json_rendered,
+        json_artifact_notifications,
+        json_unused_externs,
+        json_future_incompat,
+    } = parse_json(matches);
 
     let error_format = parse_error_format(matches, color, json_rendered);
 
@@ -2248,6 +2257,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         edition,
         json_artifact_notifications,
         json_unused_externs,
+        json_future_incompat,
         pretty,
         working_dir,
     }
index 20ef1afaab2be093a54218333adc03e62f21bc2a..bd7b1639613eb7963a776fcb38b2e779cf95cb74 100644 (file)
@@ -228,6 +228,9 @@ pub struct Options {
         /// `true` if we're emitting a JSON blob containing the unused externs
         json_unused_externs: bool [UNTRACKED],
 
+        /// `true` if we're emitting a JSON job containg a future-incompat report for lints
+        json_future_incompat: bool [TRACKED],
+
         pretty: Option<PpMode> [UNTRACKED],
 
         /// The (potentially remapped) working directory
@@ -1147,8 +1150,6 @@ mod parse {
         computed `block` spans (one span encompassing a block's terminator and \
         all statements). If `-Z instrument-coverage` is also enabled, create \
         an additional `.html` file showing the computed coverage spans."),
-    emit_future_incompat_report: bool = (false, parse_bool, [UNTRACKED],
-        "emits a future-incompatibility report for lints (RFC 2834)"),
     emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
         "emit a section containing stack size metadata (default: no)"),
     fewer_names: Option<bool> = (None, parse_opt_bool, [TRACKED],
index e37d504135dbff562e003bad51dfd95bcf8cad8b..730e79a56470ea95e23e313caaab0af9583c58f1 100644 (file)
@@ -280,7 +280,7 @@ pub fn finish_diagnostics(&self, registry: &Registry) {
     }
 
     fn emit_future_breakage(&self) {
-        if !self.opts.debugging_opts.emit_future_incompat_report {
+        if !self.opts.json_future_incompat {
             return;
         }
 
index 309c305293fd6e54e6ffe3d6af4b229d214b67bc..a4280047c70689b1bf0e1976c7547f795cdc4adc 100644 (file)
         core_panic_macro,
         cosf32,
         cosf64,
+        count,
         cr,
         crate_id,
         crate_in_paths,
         reg64,
         reg_abcd,
         reg_byte,
+        reg_iw,
         reg_nonzero,
+        reg_pair,
+        reg_ptr,
         reg_thumb,
+        reg_upper,
         register_attr,
         register_tool,
         relaxed_adts,
diff --git a/compiler/rustc_target/src/asm/avr.rs b/compiler/rustc_target/src/asm/avr.rs
new file mode 100644 (file)
index 0000000..82a4f8b
--- /dev/null
@@ -0,0 +1,196 @@
+use super::{InlineAsmArch, InlineAsmType};
+use rustc_macros::HashStable_Generic;
+use std::fmt;
+
+def_reg_class! {
+    Avr AvrInlineAsmRegClass {
+        reg,
+        reg_upper,
+        reg_pair,
+        reg_iw,
+        reg_ptr,
+    }
+}
+
+impl AvrInlineAsmRegClass {
+    pub fn valid_modifiers(self, _arch: InlineAsmArch) -> &'static [char] {
+        match self {
+            Self::reg_pair | Self::reg_iw | Self::reg_ptr => &['h', 'l'],
+            _ => &[],
+        }
+    }
+
+    pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+        None
+    }
+
+    pub fn suggest_modifier(
+        self,
+        _arch: InlineAsmArch,
+        _ty: InlineAsmType,
+    ) -> Option<(char, &'static str)> {
+        None
+    }
+
+    pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+        None
+    }
+
+    pub fn supported_types(
+        self,
+        _arch: InlineAsmArch,
+    ) -> &'static [(InlineAsmType, Option<&'static str>)] {
+        match self {
+            Self::reg => types! { _: I8; },
+            Self::reg_upper => types! { _: I8; },
+            Self::reg_pair => types! { _: I16; },
+            Self::reg_iw => types! { _: I16; },
+            Self::reg_ptr => types! { _: I16; },
+        }
+    }
+}
+
+def_regs! {
+    Avr AvrInlineAsmReg AvrInlineAsmRegClass {
+        r2: reg = ["r2"],
+        r3: reg = ["r3"],
+        r4: reg = ["r4"],
+        r5: reg = ["r5"],
+        r6: reg = ["r6"],
+        r7: reg = ["r7"],
+        r8: reg = ["r8"],
+        r9: reg = ["r9"],
+        r10: reg = ["r10"],
+        r11: reg = ["r11"],
+        r12: reg = ["r12"],
+        r13: reg = ["r13"],
+        r14: reg = ["r14"],
+        r15: reg = ["r15"],
+        r16: reg, reg_upper = ["r16"],
+        r17: reg, reg_upper = ["r17"],
+        r18: reg, reg_upper = ["r18"],
+        r19: reg, reg_upper = ["r19"],
+        r20: reg, reg_upper = ["r20"],
+        r21: reg, reg_upper = ["r21"],
+        r22: reg, reg_upper = ["r22"],
+        r23: reg, reg_upper = ["r23"],
+        r24: reg, reg_upper = ["r24"],
+        r25: reg, reg_upper = ["r25"],
+        r26: reg, reg_upper = ["r26", "XL"],
+        r27: reg, reg_upper = ["r27", "XH"],
+        r30: reg, reg_upper = ["r30", "ZL"],
+        r31: reg, reg_upper = ["r31", "ZH"],
+
+        r3r2: reg_pair = ["r3r2"],
+        r5r4: reg_pair = ["r5r4"],
+        r7r6: reg_pair = ["r7r6"],
+        r9r8: reg_pair = ["r9r8"],
+        r11r10: reg_pair = ["r11r10"],
+        r13r12: reg_pair = ["r13r12"],
+        r15r14: reg_pair = ["r15r14"],
+        r17r16: reg_pair = ["r17r16"],
+        r19r18: reg_pair = ["r19r18"],
+        r21r20: reg_pair = ["r21r20"],
+        r23r22: reg_pair = ["r23r22"],
+
+        r25r24: reg_iw, reg_pair = ["r25r24"],
+
+        X: reg_ptr, reg_iw, reg_pair = ["r27r26", "X"],
+        Z: reg_ptr, reg_iw, reg_pair = ["r31r30", "Z"],
+
+        #error = ["Y", "YL", "YH"] =>
+            "the frame pointer cannot be used as an operand for inline asm",
+        #error = ["SP", "SPL", "SPH"] =>
+            "the stack pointer cannot be used as an operand for inline asm",
+        #error = ["r0", "r1", "r1r0"] =>
+            "r0 and r1 are not available due to an issue in LLVM",
+    }
+}
+
+macro_rules! emit_pairs {
+    (
+        $self:ident $modifier:ident,
+        $($pair:ident $name:literal $hi:literal $lo:literal,)*
+    ) => {
+        match ($self, $modifier) {
+            $(
+                (AvrInlineAsmReg::$pair, Some('h')) => $hi,
+                (AvrInlineAsmReg::$pair, Some('l')) => $lo,
+                (AvrInlineAsmReg::$pair, _) => $name,
+            )*
+            _ => $self.name(),
+        }
+    };
+}
+
+impl AvrInlineAsmReg {
+    pub fn emit(
+        self,
+        out: &mut dyn fmt::Write,
+        _arch: InlineAsmArch,
+        modifier: Option<char>,
+    ) -> fmt::Result {
+        let name = emit_pairs! {
+            self modifier,
+            Z "Z" "ZH" "ZL",
+            X "X" "XH" "XL",
+            r25r24 "r25:r24" "r25" "r24",
+            r23r22 "r23:r22" "r23" "r22",
+            r21r20 "r21:r20" "r21" "r20",
+            r19r18 "r19:r18" "r19" "r18",
+            r17r16 "r17:r16" "r17" "r16",
+            r15r14 "r15:r14" "r15" "r14",
+            r13r12 "r13:r12" "r13" "r12",
+            r11r10 "r11:r10" "r11" "r10",
+            r9r8 "r9:r8" "r9" "r8",
+            r7r6 "r7:r6" "r7" "r6",
+            r5r4 "r5:r4" "r5" "r4",
+            r3r2 "r3:r2" "r3" "r2",
+        };
+        out.write_str(name)
+    }
+
+    pub fn overlapping_regs(self, mut cb: impl FnMut(AvrInlineAsmReg)) {
+        cb(self);
+
+        macro_rules! reg_conflicts {
+            (
+                $(
+                    $pair:ident : $hi:ident $lo:ident,
+                )*
+            ) => {
+                match self {
+                    $(
+                        Self::$pair => {
+                            cb(Self::$hi);
+                            cb(Self::$lo);
+                        }
+                        Self::$hi => {
+                            cb(Self::$pair);
+                        }
+                        Self::$lo => {
+                            cb(Self::$pair);
+                        }
+                    )*
+                }
+            };
+        }
+
+        reg_conflicts! {
+            Z : r31 r30,
+            X : r27 r26,
+            r25r24 : r25 r24,
+            r23r22 : r23 r22,
+            r21r20 : r21 r20,
+            r19r18 : r19 r18,
+            r17r16 : r17 r16,
+            r15r14 : r15 r14,
+            r13r12 : r13 r12,
+            r11r10 : r11 r10,
+            r9r8 : r9 r8,
+            r7r6 : r7 r6,
+            r5r4 : r5 r4,
+            r3r2 : r3 r2,
+        }
+    }
+}
index bff132465215eda9d247eb56cd68e7546532725c..cf940594bc459a616bbdac3298b836f0bcd14a75 100644 (file)
@@ -148,6 +148,7 @@ macro_rules! types {
 
 mod aarch64;
 mod arm;
+mod avr;
 mod bpf;
 mod hexagon;
 mod mips;
@@ -161,6 +162,7 @@ macro_rules! types {
 
 pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass};
 pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass};
+pub use avr::{AvrInlineAsmReg, AvrInlineAsmRegClass};
 pub use bpf::{BpfInlineAsmReg, BpfInlineAsmRegClass};
 pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass};
 pub use mips::{MipsInlineAsmReg, MipsInlineAsmRegClass};
@@ -191,6 +193,7 @@ pub enum InlineAsmArch {
     Wasm32,
     Wasm64,
     Bpf,
+    Avr,
 }
 
 impl FromStr for InlineAsmArch {
@@ -215,6 +218,7 @@ fn from_str(s: &str) -> Result<InlineAsmArch, ()> {
             "wasm32" => Ok(Self::Wasm32),
             "wasm64" => Ok(Self::Wasm64),
             "bpf" => Ok(Self::Bpf),
+            "avr" => Ok(Self::Avr),
             _ => Err(()),
         }
     }
@@ -245,6 +249,7 @@ pub enum InlineAsmReg {
     SpirV(SpirVInlineAsmReg),
     Wasm(WasmInlineAsmReg),
     Bpf(BpfInlineAsmReg),
+    Avr(AvrInlineAsmReg),
     // Placeholder for invalid register constraints for the current target
     Err,
 }
@@ -261,6 +266,7 @@ pub fn name(self) -> &'static str {
             Self::Mips(r) => r.name(),
             Self::S390x(r) => r.name(),
             Self::Bpf(r) => r.name(),
+            Self::Avr(r) => r.name(),
             Self::Err => "<reg>",
         }
     }
@@ -276,6 +282,7 @@ pub fn reg_class(self) -> InlineAsmRegClass {
             Self::Mips(r) => InlineAsmRegClass::Mips(r.reg_class()),
             Self::S390x(r) => InlineAsmRegClass::S390x(r.reg_class()),
             Self::Bpf(r) => InlineAsmRegClass::Bpf(r.reg_class()),
+            Self::Avr(r) => InlineAsmRegClass::Avr(r.reg_class()),
             Self::Err => InlineAsmRegClass::Err,
         }
     }
@@ -326,6 +333,9 @@ pub fn parse(
             InlineAsmArch::Bpf => {
                 Self::Bpf(BpfInlineAsmReg::parse(arch, has_feature, target, &name)?)
             }
+            InlineAsmArch::Avr => {
+                Self::Avr(AvrInlineAsmReg::parse(arch, has_feature, target, &name)?)
+            }
         })
     }
 
@@ -347,6 +357,7 @@ pub fn emit(
             Self::Mips(r) => r.emit(out, arch, modifier),
             Self::S390x(r) => r.emit(out, arch, modifier),
             Self::Bpf(r) => r.emit(out, arch, modifier),
+            Self::Avr(r) => r.emit(out, arch, modifier),
             Self::Err => unreachable!("Use of InlineAsmReg::Err"),
         }
     }
@@ -362,6 +373,7 @@ pub fn overlapping_regs(self, mut cb: impl FnMut(InlineAsmReg)) {
             Self::Mips(_) => cb(self),
             Self::S390x(_) => cb(self),
             Self::Bpf(r) => r.overlapping_regs(|r| cb(Self::Bpf(r))),
+            Self::Avr(r) => r.overlapping_regs(|r| cb(Self::Avr(r))),
             Self::Err => unreachable!("Use of InlineAsmReg::Err"),
         }
     }
@@ -392,6 +404,7 @@ pub enum InlineAsmRegClass {
     SpirV(SpirVInlineAsmRegClass),
     Wasm(WasmInlineAsmRegClass),
     Bpf(BpfInlineAsmRegClass),
+    Avr(AvrInlineAsmRegClass),
     // Placeholder for invalid register constraints for the current target
     Err,
 }
@@ -411,6 +424,7 @@ pub fn name(self) -> Symbol {
             Self::SpirV(r) => r.name(),
             Self::Wasm(r) => r.name(),
             Self::Bpf(r) => r.name(),
+            Self::Avr(r) => r.name(),
             Self::Err => rustc_span::symbol::sym::reg,
         }
     }
@@ -432,6 +446,7 @@ pub fn suggest_class(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option<Sel
             Self::SpirV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::SpirV),
             Self::Wasm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Wasm),
             Self::Bpf(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Bpf),
+            Self::Avr(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Avr),
             Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
         }
     }
@@ -460,6 +475,7 @@ pub fn suggest_modifier(
             Self::SpirV(r) => r.suggest_modifier(arch, ty),
             Self::Wasm(r) => r.suggest_modifier(arch, ty),
             Self::Bpf(r) => r.suggest_modifier(arch, ty),
+            Self::Avr(r) => r.suggest_modifier(arch, ty),
             Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
         }
     }
@@ -484,6 +500,7 @@ pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str
             Self::SpirV(r) => r.default_modifier(arch),
             Self::Wasm(r) => r.default_modifier(arch),
             Self::Bpf(r) => r.default_modifier(arch),
+            Self::Avr(r) => r.default_modifier(arch),
             Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
         }
     }
@@ -507,6 +524,7 @@ pub fn supported_types(
             Self::SpirV(r) => r.supported_types(arch),
             Self::Wasm(r) => r.supported_types(arch),
             Self::Bpf(r) => r.supported_types(arch),
+            Self::Avr(r) => r.supported_types(arch),
             Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
         }
     }
@@ -535,6 +553,7 @@ pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result<Self, &'static str> {
                 Self::Wasm(WasmInlineAsmRegClass::parse(arch, name)?)
             }
             InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmRegClass::parse(arch, name)?),
+            InlineAsmArch::Avr => Self::Avr(AvrInlineAsmRegClass::parse(arch, name)?),
         })
     }
 
@@ -554,6 +573,7 @@ pub fn valid_modifiers(self, arch: InlineAsmArch) -> &'static [char] {
             Self::SpirV(r) => r.valid_modifiers(arch),
             Self::Wasm(r) => r.valid_modifiers(arch),
             Self::Bpf(r) => r.valid_modifiers(arch),
+            Self::Avr(r) => r.valid_modifiers(arch),
             Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
         }
     }
@@ -739,6 +759,11 @@ pub fn allocatable_registers(
             bpf::fill_reg_map(arch, has_feature, target, &mut map);
             map
         }
+        InlineAsmArch::Avr => {
+            let mut map = avr::regclass_map();
+            avr::fill_reg_map(arch, has_feature, target, &mut map);
+            map
+        }
     }
 }
 
index fff7b25a34937c6c505bfe84a3f21dcccfefe89e..5671b59c63fda700ea6229331d733f637fefc93e 100644 (file)
@@ -2,7 +2,7 @@
 use crate::spec::{Target, TargetOptions};
 
 pub fn target() -> Target {
-    let mut base = super::linux_base::opts();
+    let mut base = super::linux_gnu_base::opts();
     base.max_atomic_width = Some(32);
 
     Target {
index 72d9b5015451cd3ba14e9d5096e2a97eccce7a7a..ece704d77000324f1ffe9b1676227e9ce68ff1e5 100644 (file)
@@ -867,6 +867,7 @@ fn $module() {
     ("powerpc-unknown-freebsd", powerpc_unknown_freebsd),
     ("powerpc64-unknown-freebsd", powerpc64_unknown_freebsd),
     ("powerpc64le-unknown-freebsd", powerpc64le_unknown_freebsd),
+    ("riscv64gc-unknown-freebsd", riscv64gc_unknown_freebsd),
     ("x86_64-unknown-freebsd", x86_64_unknown_freebsd),
 
     ("x86_64-unknown-dragonfly", x86_64_unknown_dragonfly),
diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_freebsd.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_freebsd.rs
new file mode 100644 (file)
index 0000000..1ea1b9b
--- /dev/null
@@ -0,0 +1,18 @@
+use crate::spec::{CodeModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+    Target {
+        llvm_target: "riscv64-unknown-freebsd".to_string(),
+        pointer_width: 64,
+        data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".to_string(),
+        arch: "riscv64".to_string(),
+        options: TargetOptions {
+            code_model: Some(CodeModel::Medium),
+            cpu: "generic-rv64".to_string(),
+            features: "+m,+a,+f,+d,+c".to_string(),
+            llvm_abiname: "lp64d".to_string(),
+            max_atomic_width: Some(64),
+            ..super::freebsd_base::opts()
+        },
+    }
+}
index 6b5d37c0f43086516d57425d66807dd3b6ca72b7..0ea3a18ca34fa2af5317beb4e552dc15594a4e0d 100644 (file)
@@ -399,13 +399,25 @@ fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorReported>
                 let arg = self.recurse_build(source)?;
                 self.nodes.push(Node::Cast(abstract_const::CastKind::As, arg, node.ty))
             }
-
+            ExprKind::Borrow{ arg, ..} => {
+                let arg_node = &self.body.exprs[*arg];
+
+                // Skip reborrows for now until we allow Deref/Borrow/AddressOf
+                // expressions.
+                // FIXME(generic_const_exprs): Verify/explain why this is sound
+                if let ExprKind::Deref {arg} = arg_node.kind {
+                    self.recurse_build(arg)?
+                } else {
+                    self.maybe_supported_error(
+                        node.span,
+                        "borrowing is not supported in generic constants",
+                    )?
+                }
+            }
             // FIXME(generic_const_exprs): We may want to support these.
-            ExprKind::AddressOf { .. }
-            | ExprKind::Borrow { .. }
-            | ExprKind::Deref { .. } => self.maybe_supported_error(
+            ExprKind::AddressOf { .. } | ExprKind::Deref {..}=> self.maybe_supported_error(
                 node.span,
-                "dereferencing is not supported in generic constants",
+                "dereferencing or taking the address is not supported in generic constants",
             )?,
             ExprKind::Repeat { .. } | ExprKind::Array { .. } =>  self.maybe_supported_error(
                 node.span,
index 2910ce6de689965c4adcde2668aedab785443790..37e601fa40442e9bec20b50650bbfaccd6e74609 100644 (file)
@@ -536,22 +536,28 @@ pub fn check_must_not_suspend_ty<'tcx>(
             }
             has_emitted
         }
-        ty::Tuple(ref tys) => {
+        ty::Tuple(_) => {
             let mut has_emitted = false;
-            let spans = if let Some(hir::ExprKind::Tup(comps)) = data.expr.map(|e| &e.kind) {
-                debug_assert_eq!(comps.len(), tys.len());
-                comps.iter().map(|e| e.span).collect()
-            } else {
-                vec![]
+            let comps = match data.expr.map(|e| &e.kind) {
+                Some(hir::ExprKind::Tup(comps)) => {
+                    debug_assert_eq!(comps.len(), ty.tuple_fields().count());
+                    Some(comps)
+                }
+                _ => None,
             };
-            for (i, ty) in tys.iter().map(|k| k.expect_ty()).enumerate() {
+            for (i, ty) in ty.tuple_fields().enumerate() {
                 let descr_post = &format!(" in tuple element {}", i);
-                let span = *spans.get(i).unwrap_or(&data.source_span);
+                let span = comps.and_then(|c| c.get(i)).map(|e| e.span).unwrap_or(data.source_span);
                 if check_must_not_suspend_ty(
                     fcx,
                     ty,
                     hir_id,
-                    SuspendCheckData { descr_post, source_span: span, ..data },
+                    SuspendCheckData {
+                        descr_post,
+                        expr: comps.and_then(|comps| comps.get(i)),
+                        source_span: span,
+                        ..data
+                    },
                 ) {
                     has_emitted = true;
                 }
index ca174ed5e84972cb3950e301e6bd3c7a107be699..ad38885dbd8bdf86c917b28e814faf8872a36b0e 100644 (file)
@@ -67,6 +67,10 @@ fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
         }
     }
 
+    fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
+        self.autoderef(span, ty).any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..)))
+    }
+
     pub fn report_method_error(
         &self,
         mut span: Span,
@@ -691,7 +695,23 @@ fn report_function<T: std::fmt::Display>(
 
                 let mut restrict_type_params = false;
                 let mut unsatisfied_bounds = false;
-                if !unsatisfied_predicates.is_empty() {
+                if item_name.name == sym::count && self.is_slice_ty(actual, span) {
+                    let msg = "consider using `len` instead";
+                    if let SelfSource::MethodCall(_expr) = source {
+                        err.span_suggestion_short(
+                            span,
+                            msg,
+                            String::from("len"),
+                            Applicability::MachineApplicable,
+                        );
+                    } else {
+                        err.span_label(span, msg);
+                    }
+                    if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) {
+                        let iterator_trait = self.tcx.def_path_str(iterator_trait);
+                        err.note(&format!("`count` is defined on `{iterator_trait}`, which `{actual}` does not implement"));
+                    }
+                } else if !unsatisfied_predicates.is_empty() {
                     let def_span = |def_id| {
                         self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id))
                     };
@@ -990,9 +1010,9 @@ trait bound{s}",
                     }
                 }
 
-                let mut fallback_span = true;
-                let msg = "remove this method call";
                 if item_name.name == sym::as_str && actual.peel_refs().is_str() {
+                    let msg = "remove this method call";
+                    let mut fallback_span = true;
                     if let SelfSource::MethodCall(expr) = source {
                         let call_expr =
                             self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
index f83209f57a897cf54145fd76295746c45faa4e65..4956321eb5cd49ed26e897e3ef0839c76b03305b 100644 (file)
@@ -492,7 +492,6 @@ fn add_type_neq_err_label(
     ) -> bool /* did we suggest to call a function because of missing parentheses? */ {
         err.span_label(span, ty.to_string());
         if let FnDef(def_id, _) = *ty.kind() {
-            let source_map = self.tcx.sess.source_map();
             if !self.tcx.has_typeck_results(def_id) {
                 return false;
             }
@@ -517,20 +516,18 @@ fn add_type_neq_err_label(
                 .lookup_op_method(fn_sig.output(), &[other_ty], Op::Binary(op, is_assign))
                 .is_ok()
             {
-                if let Ok(snippet) = source_map.span_to_snippet(span) {
-                    let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() {
-                        (format!("{}( /* arguments */ )", snippet), Applicability::HasPlaceholders)
-                    } else {
-                        (format!("{}()", snippet), Applicability::MaybeIncorrect)
-                    };
+                let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() {
+                    ("( /* arguments */ )".to_string(), Applicability::HasPlaceholders)
+                } else {
+                    ("()".to_string(), Applicability::MaybeIncorrect)
+                };
 
-                    err.span_suggestion(
-                        span,
-                        "you might have forgotten to call this function",
-                        variable_snippet,
-                        applicability,
-                    );
-                }
+                err.span_suggestion_verbose(
+                    span.shrink_to_hi(),
+                    "you might have forgotten to call this function",
+                    variable_snippet,
+                    applicability,
+                );
                 return true;
             }
         }
index eaa69b7eb608646ba4cb8a82086edf60fc3603ab..67a3053c60786b60303a0126e922edfdfe621f12 100644 (file)
@@ -41,7 +41,7 @@
 use rustc_middle::ty::util::Discr;
 use rustc_middle::ty::util::IntTypeExt;
 use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, Ty, TyCtxt};
-use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness};
+use rustc_middle::ty::{ReprOptions, ToPredicate, TypeFoldable, WithConstness};
 use rustc_session::lint;
 use rustc_session::parse::feature_err;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -1777,7 +1777,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
                     visitor.visit_ty(ty);
                     let mut diag = bad_placeholder_type(tcx, visitor.0, "return type");
                     let ret_ty = fn_sig.skip_binder().output();
-                    if ret_ty != tcx.ty_error() {
+                    if !ret_ty.references_error() {
                         if !ret_ty.is_closure() {
                             let ret_ty_str = match ret_ty.kind() {
                                 // Suggest a function pointer return type instead of a unique function definition
index 04a68250ced0cd0a44387b6fa7d3abf868b03351..b684844744de3c4d49729fb75db2e81ef273d129 100644 (file)
@@ -172,7 +172,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
                 // We've encountered an `AnonConst` in some path, so we need to
                 // figure out which generic parameter it corresponds to and return
                 // the relevant type.
-                let (arg_index, segment) = path
+                let filtered = path
                     .segments
                     .iter()
                     .filter_map(|seg| seg.args.map(|args| (args.args, seg)))
@@ -181,10 +181,17 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
                             .filter(|arg| arg.is_const())
                             .position(|arg| arg.id() == hir_id)
                             .map(|index| (index, seg))
-                    })
-                    .unwrap_or_else(|| {
-                        bug!("no arg matching AnonConst in path");
                     });
+                let (arg_index, segment) = match filtered {
+                    None => {
+                        tcx.sess.delay_span_bug(
+                            tcx.def_span(def_id),
+                            "no arg matching AnonConst in path",
+                        );
+                        return None;
+                    }
+                    Some(inner) => inner,
+                };
 
                 // Try to use the segment resolution if it is valid, otherwise we
                 // default to the path resolution.
index 42eae6a54b5311c1e6d9a3c22c6982a70bf9e207..702d97858eb45a43d2e6d3b277ec599a17443bc2 100644 (file)
@@ -720,9 +720,9 @@ pub fn reserve(&mut self, additional: usize) {
     ///
     /// Note that the allocator may give the collection more space than it
     /// requests. Therefore, capacity can not be relied upon to be precisely
-    /// minimal. Prefer [`reserve`] if future insertions are expected.
+    /// minimal. Prefer [`try_reserve`] if future insertions are expected.
     ///
-    /// [`reserve`]: VecDeque::reserve
+    /// [`try_reserve`]: VecDeque::try_reserve
     ///
     /// # Errors
     ///
index 4f926d99c6dbc46e992aa73d4b89b6cd82852a11..b151842458d355789feaac777ac36da32be195b2 100644 (file)
@@ -1044,9 +1044,9 @@ pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>
     ///
     /// Note that the allocator may give the collection more space than it
     /// requests. Therefore, capacity can not be relied upon to be precisely
-    /// minimal. Prefer [`reserve`] if future insertions are expected.
+    /// minimal. Prefer [`try_reserve`] if future insertions are expected.
     ///
-    /// [`reserve`]: String::reserve
+    /// [`try_reserve`]: String::try_reserve
     ///
     /// # Errors
     ///
index 88bde6e8ce48152f1aad625911bc91e2578663e8..f1b70fa280214364843eebec90683a90585998ff 100644 (file)
@@ -881,9 +881,9 @@ pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>
     ///
     /// Note that the allocator may give the collection more space than it
     /// requests. Therefore, capacity can not be relied upon to be precisely
-    /// minimal. Prefer [`reserve`] if future insertions are expected.
+    /// minimal. Prefer [`try_reserve`] if future insertions are expected.
     ///
-    /// [`reserve`]: Vec::reserve
+    /// [`try_reserve`]: Vec::try_reserve
     ///
     /// # Errors
     ///
index fe7b3576e2f5fdd2b770e93e53a21df7bd18d167..72a634443e8776034552e2a0decd3f03d7b6ec90 100644 (file)
@@ -1,7 +1,7 @@
 //! Defines the `IntoIter` owned iterator for arrays.
 
 use crate::{
-    fmt,
+    cmp, fmt,
     iter::{self, ExactSizeIterator, FusedIterator, TrustedLen},
     mem::{self, MaybeUninit},
     ops::Range,
@@ -84,6 +84,135 @@ impl<T, const N: usize> IntoIter<T, N> {
         IntoIterator::into_iter(array)
     }
 
+    /// Creates an iterator over the elements in a partially-initialized buffer.
+    ///
+    /// If you have a fully-initialized array, then use [`IntoIterator`].
+    /// But this is useful for returning partial results from unsafe code.
+    ///
+    /// # Safety
+    ///
+    /// - The `buffer[initialized]` elements must all be initialized.
+    /// - The range must be canonical, with `initialized.start <= initialized.end`.
+    /// - The range must in in-bounds for the buffer, with `initialized.end <= N`.
+    ///   (Like how indexing `[0][100..100]` fails despite the range being empty.)
+    ///
+    /// It's sound to have more elements initialized than mentioned, though that
+    /// will most likely result in them being leaked.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(array_into_iter_constructors)]
+    ///
+    /// #![feature(maybe_uninit_array_assume_init)]
+    /// #![feature(maybe_uninit_uninit_array)]
+    /// use std::array::IntoIter;
+    /// use std::mem::MaybeUninit;
+    ///
+    /// # // Hi!  Thanks for reading the code.  This is restricted to `Copy` because
+    /// # // otherwise it could leak.  A fully-general version this would need a drop
+    /// # // guard to handle panics from the iterator, but this works for an example.
+    /// fn next_chunk<T: Copy, const N: usize>(
+    ///     it: &mut impl Iterator<Item = T>,
+    /// ) -> Result<[T; N], IntoIter<T, N>> {
+    ///     let mut buffer = MaybeUninit::uninit_array();
+    ///     let mut i = 0;
+    ///     while i < N {
+    ///         match it.next() {
+    ///             Some(x) => {
+    ///                 buffer[i].write(x);
+    ///                 i += 1;
+    ///             }
+    ///             None => {
+    ///                 // SAFETY: We've initialized the first `i` items
+    ///                 unsafe {
+    ///                     return Err(IntoIter::new_unchecked(buffer, 0..i));
+    ///                 }
+    ///             }
+    ///         }
+    ///     }
+    ///
+    ///     // SAFETY: We've initialized all N items
+    ///     unsafe { Ok(MaybeUninit::array_assume_init(buffer)) }
+    /// }
+    ///
+    /// let r: [_; 4] = next_chunk(&mut (10..16)).unwrap();
+    /// assert_eq!(r, [10, 11, 12, 13]);
+    /// let r: IntoIter<_, 40> = next_chunk(&mut (10..16)).unwrap_err();
+    /// assert_eq!(r.collect::<Vec<_>>(), vec![10, 11, 12, 13, 14, 15]);
+    /// ```
+    #[unstable(feature = "array_into_iter_constructors", issue = "91583")]
+    #[rustc_const_unstable(feature = "const_array_into_iter_constructors", issue = "91583")]
+    pub const unsafe fn new_unchecked(
+        buffer: [MaybeUninit<T>; N],
+        initialized: Range<usize>,
+    ) -> Self {
+        Self { data: buffer, alive: initialized }
+    }
+
+    /// Creates an iterator over `T` which returns no elements.
+    ///
+    /// If you just need an empty iterator, then use
+    /// [`iter::empty()`](crate::iter::empty) instead.
+    /// And if you need an empty array, use `[]`.
+    ///
+    /// But this is useful when you need an `array::IntoIter<T, N>` *specifically*.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(array_into_iter_constructors)]
+    /// use std::array::IntoIter;
+    ///
+    /// let empty = IntoIter::<i32, 3>::empty();
+    /// assert_eq!(empty.len(), 0);
+    /// assert_eq!(empty.as_slice(), &[]);
+    ///
+    /// let empty = IntoIter::<std::convert::Infallible, 200>::empty();
+    /// assert_eq!(empty.len(), 0);
+    /// ```
+    ///
+    /// `[1, 2].into_iter()` and `[].into_iter()` have different types
+    /// ```should_fail,edition2021
+    /// #![feature(array_into_iter_constructors)]
+    /// use std::array::IntoIter;
+    ///
+    /// pub fn get_bytes(b: bool) -> IntoIter<i8, 4> {
+    ///     if b {
+    ///         [1, 2, 3, 4].into_iter()
+    ///     } else {
+    ///         [].into_iter() // error[E0308]: mismatched types
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// But using this method you can get an empty iterator of appropriate size:
+    /// ```edition2021
+    /// #![feature(array_into_iter_constructors)]
+    /// use std::array::IntoIter;
+    ///
+    /// pub fn get_bytes(b: bool) -> IntoIter<i8, 4> {
+    ///     if b {
+    ///         [1, 2, 3, 4].into_iter()
+    ///     } else {
+    ///         IntoIter::empty()
+    ///     }
+    /// }
+    ///
+    /// assert_eq!(get_bytes(true).collect::<Vec<_>>(), vec![1, 2, 3, 4]);
+    /// assert_eq!(get_bytes(false).collect::<Vec<_>>(), vec![]);
+    /// ```
+    #[unstable(feature = "array_into_iter_constructors", issue = "91583")]
+    #[rustc_const_unstable(feature = "const_array_into_iter_constructors", issue = "91583")]
+    pub const fn empty() -> Self {
+        let buffer = MaybeUninit::uninit_array();
+        let initialized = 0..0;
+
+        // SAFETY: We're telling it that none of the elements are initialized,
+        // which is trivially true.  And ∀N: usize, 0 <= N.
+        unsafe { Self::new_unchecked(buffer, initialized) }
+    }
+
     /// Returns an immutable slice of all elements that have not been yielded
     /// yet.
     #[stable(feature = "array_value_iter", since = "1.51.0")]
@@ -152,6 +281,27 @@ fn count(self) -> usize {
     fn last(mut self) -> Option<Self::Item> {
         self.next_back()
     }
+
+    fn advance_by(&mut self, n: usize) -> Result<(), usize> {
+        let len = self.len();
+
+        // The number of elements to drop.  Always in-bounds by construction.
+        let delta = cmp::min(n, len);
+
+        let range_to_drop = self.alive.start..(self.alive.start + delta);
+
+        // Moving the start marks them as conceptually "dropped", so if anything
+        // goes bad then our drop impl won't double-free them.
+        self.alive.start += delta;
+
+        // SAFETY: These elements are currently initialized, so it's fine to drop them.
+        unsafe {
+            let slice = self.data.get_unchecked_mut(range_to_drop);
+            ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(slice));
+        }
+
+        if n > len { Err(len) } else { Ok(()) }
+    }
 }
 
 #[stable(feature = "array_value_iter_impls", since = "1.40.0")]
@@ -172,6 +322,27 @@ fn next_back(&mut self) -> Option<Self::Item> {
             unsafe { self.data.get_unchecked(idx).assume_init_read() }
         })
     }
+
+    fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
+        let len = self.len();
+
+        // The number of elements to drop.  Always in-bounds by construction.
+        let delta = cmp::min(n, len);
+
+        let range_to_drop = (self.alive.end - delta)..self.alive.end;
+
+        // Moving the end marks them as conceptually "dropped", so if anything
+        // goes bad then our drop impl won't double-free them.
+        self.alive.end -= delta;
+
+        // SAFETY: These elements are currently initialized, so it's fine to drop them.
+        unsafe {
+            let slice = self.data.get_unchecked_mut(range_to_drop);
+            ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(slice));
+        }
+
+        if n > len { Err(len) } else { Ok(()) }
+    }
 }
 
 #[stable(feature = "array_value_iter_impls", since = "1.40.0")]
index b0f9368b0c068587764a754bad5ced58c91dbbf8..78383b54c5d1e410b7f02557a0fb6e5852a8869b 100644 (file)
 #![feature(const_align_of_val)]
 #![feature(const_alloc_layout)]
 #![feature(const_arguments_as_str)]
+#![feature(const_array_into_iter_constructors)]
 #![feature(const_bigint_helper_methods)]
 #![feature(const_caller_location)]
 #![feature(const_cell_into_inner)]
 #![feature(const_type_name)]
 #![feature(const_default_impls)]
 #![feature(duration_consts_float)]
+#![feature(maybe_uninit_uninit_array)]
 #![feature(ptr_metadata)]
 #![feature(slice_ptr_get)]
 #![feature(str_internals)]
index 43d30d12355eb491166d4ce7431df6590b15fbb5..d212a3a3a05da89594203376d7e4396e9582a3c4 100644 (file)
@@ -474,3 +474,109 @@ fn array_split_array_mut_out_of_bounds() {
 
     v.split_array_mut::<7>();
 }
+
+#[test]
+fn array_intoiter_advance_by() {
+    use std::cell::Cell;
+    struct DropCounter<'a>(usize, &'a Cell<usize>);
+    impl Drop for DropCounter<'_> {
+        fn drop(&mut self) {
+            let x = self.1.get();
+            self.1.set(x + 1);
+        }
+    }
+
+    let counter = Cell::new(0);
+    let a: [_; 100] = std::array::from_fn(|i| DropCounter(i, &counter));
+    let mut it = IntoIterator::into_iter(a);
+
+    let r = it.advance_by(1);
+    assert_eq!(r, Ok(()));
+    assert_eq!(it.len(), 99);
+    assert_eq!(counter.get(), 1);
+
+    let r = it.advance_by(0);
+    assert_eq!(r, Ok(()));
+    assert_eq!(it.len(), 99);
+    assert_eq!(counter.get(), 1);
+
+    let r = it.advance_by(11);
+    assert_eq!(r, Ok(()));
+    assert_eq!(it.len(), 88);
+    assert_eq!(counter.get(), 12);
+
+    let x = it.next();
+    assert_eq!(x.as_ref().map(|x| x.0), Some(12));
+    assert_eq!(it.len(), 87);
+    assert_eq!(counter.get(), 12);
+    drop(x);
+    assert_eq!(counter.get(), 13);
+
+    let r = it.advance_by(123456);
+    assert_eq!(r, Err(87));
+    assert_eq!(it.len(), 0);
+    assert_eq!(counter.get(), 100);
+
+    let r = it.advance_by(0);
+    assert_eq!(r, Ok(()));
+    assert_eq!(it.len(), 0);
+    assert_eq!(counter.get(), 100);
+
+    let r = it.advance_by(10);
+    assert_eq!(r, Err(0));
+    assert_eq!(it.len(), 0);
+    assert_eq!(counter.get(), 100);
+}
+
+#[test]
+fn array_intoiter_advance_back_by() {
+    use std::cell::Cell;
+    struct DropCounter<'a>(usize, &'a Cell<usize>);
+    impl Drop for DropCounter<'_> {
+        fn drop(&mut self) {
+            let x = self.1.get();
+            self.1.set(x + 1);
+        }
+    }
+
+    let counter = Cell::new(0);
+    let a: [_; 100] = std::array::from_fn(|i| DropCounter(i, &counter));
+    let mut it = IntoIterator::into_iter(a);
+
+    let r = it.advance_back_by(1);
+    assert_eq!(r, Ok(()));
+    assert_eq!(it.len(), 99);
+    assert_eq!(counter.get(), 1);
+
+    let r = it.advance_back_by(0);
+    assert_eq!(r, Ok(()));
+    assert_eq!(it.len(), 99);
+    assert_eq!(counter.get(), 1);
+
+    let r = it.advance_back_by(11);
+    assert_eq!(r, Ok(()));
+    assert_eq!(it.len(), 88);
+    assert_eq!(counter.get(), 12);
+
+    let x = it.next_back();
+    assert_eq!(x.as_ref().map(|x| x.0), Some(87));
+    assert_eq!(it.len(), 87);
+    assert_eq!(counter.get(), 12);
+    drop(x);
+    assert_eq!(counter.get(), 13);
+
+    let r = it.advance_back_by(123456);
+    assert_eq!(r, Err(87));
+    assert_eq!(it.len(), 0);
+    assert_eq!(counter.get(), 100);
+
+    let r = it.advance_back_by(0);
+    assert_eq!(r, Ok(()));
+    assert_eq!(it.len(), 0);
+    assert_eq!(counter.get(), 100);
+
+    let r = it.advance_back_by(10);
+    assert_eq!(r, Err(0));
+    assert_eq!(it.len(), 0);
+    assert_eq!(counter.get(), 100);
+}
index 50c92968c9d82b1369b130a68cd0ef5bbaa41d71..b84f3228e780039362a1d854f6ca3c8b99b47a60 100644 (file)
@@ -7,7 +7,7 @@ fn testing() {
     let x = f32x4::from_array([1.0, 1.0, 1.0, 1.0]);
     let y = -x;
 
-    let h = x * 0.5;
+    let h = x * f32x4::splat(0.5);
 
     let r = y.abs();
     assert_eq!(x, r);
index f9ba12d3a1b3adb53ddd6f321837a60f5f7860e7..9612fe871c619535792457795628fc7b687f16b1 100644 (file)
@@ -15,7 +15,7 @@ SIMD can be quite complex, and even a "simple" issue can be huge. If an issue is
 
 ## CI
 
-We currently have 2 CI matrices through Travis CI and GitHub Actions that will automatically build and test your change in order to verify that `std::simd`'s portable API is, in fact, portable. If your change builds locally, but does not build on either, this is likely due to a platform-specific concern that your code has not addressed. Please consult the build logs and address the error, or ask for help if you need it.
+We currently use GitHub Actions which will automatically build and test your change in order to verify that `std::simd`'s portable API is, in fact, portable. If your change builds locally, but does not build in CI, this is likely due to a platform-specific concern that your code has not addressed. Please consult the build logs and address the error, or ask for help if you need it.
 
 ## Beyond stdsimd
 
index da536a4d6f2d5f2133e92cbf3da6e1127eb888e6..db0af2da60641d4685d14589e882eccd534c3376 100644 (file)
@@ -1,5 +1,5 @@
 # The Rust standard library's portable SIMD API
-[![Build Status](https://travis-ci.com/rust-lang/portable-simd.svg?branch=master)](https://travis-ci.com/rust-lang/portable-simd)
+![Build Status](https://github.com/rust-lang/portable-simd/actions/workflows/ci.yml/badge.svg?branch=master)
 
 Code repository for the [Portable SIMD Project Group](https://github.com/rust-lang/project-portable-simd).
 Please refer to [CONTRIBUTING.md](./CONTRIBUTING.md) for our contributing guidelines.
index 779575985ed9ed933b67cf4ea1b503e5201950e6..43280feebbd67cdd14ed33a08b579881118d3aa2 100644 (file)
@@ -97,7 +97,7 @@ struct Body {
         let sun = &mut sun[0];
         for body in rest {
             let m_ratio = body.mass / SOLAR_MASS;
-            sun.v -= body.v * m_ratio;
+            sun.v -= body.v * Simd::splat(m_ratio);
         }
     }
 
@@ -143,14 +143,14 @@ struct Body {
         let mut i = 0;
         for j in 0..N_BODIES {
             for k in j + 1..N_BODIES {
-                let f = r[i] * mag[i];
-                bodies[j].v -= f * bodies[k].mass;
-                bodies[k].v += f * bodies[j].mass;
+                let f = r[i] * Simd::splat(mag[i]);
+                bodies[j].v -= f * Simd::splat(bodies[k].mass);
+                bodies[k].v += f * Simd::splat(bodies[j].mass);
                 i += 1
             }
         }
         for body in bodies {
-            body.x += dt * body.v
+            body.x += Simd::splat(dt) * body.v
         }
     }
 
index 8c51baca8ede38efa9f8faa26d1a51238d6049fb..edef5af3687a27f4cd20099bdb6368ed4362d59d 100644 (file)
@@ -8,12 +8,14 @@ impl<T, const LANES: usize> Simd<T, LANES>
 {
     /// Test if each lane is equal to the corresponding lane in `other`.
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub fn lanes_eq(self, other: Self) -> Mask<T::Mask, LANES> {
         unsafe { Mask::from_int_unchecked(intrinsics::simd_eq(self, other)) }
     }
 
     /// Test if each lane is not equal to the corresponding lane in `other`.
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub fn lanes_ne(self, other: Self) -> Mask<T::Mask, LANES> {
         unsafe { Mask::from_int_unchecked(intrinsics::simd_ne(self, other)) }
     }
@@ -26,24 +28,28 @@ impl<T, const LANES: usize> Simd<T, LANES>
 {
     /// Test if each lane is less than the corresponding lane in `other`.
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub fn lanes_lt(self, other: Self) -> Mask<T::Mask, LANES> {
         unsafe { Mask::from_int_unchecked(intrinsics::simd_lt(self, other)) }
     }
 
     /// Test if each lane is greater than the corresponding lane in `other`.
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub fn lanes_gt(self, other: Self) -> Mask<T::Mask, LANES> {
         unsafe { Mask::from_int_unchecked(intrinsics::simd_gt(self, other)) }
     }
 
     /// Test if each lane is less than or equal to the corresponding lane in `other`.
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub fn lanes_le(self, other: Self) -> Mask<T::Mask, LANES> {
         unsafe { Mask::from_int_unchecked(intrinsics::simd_le(self, other)) }
     }
 
     /// Test if each lane is greater than or equal to the corresponding lane in `other`.
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub fn lanes_ge(self, other: Self) -> Mask<T::Mask, LANES> {
         unsafe { Mask::from_int_unchecked(intrinsics::simd_ge(self, other)) }
     }
index b017e7d137e308293c8ecd921674d33794565e27..3b316f12b3e56f50216975c555fac45cb2f6b8f8 100644 (file)
@@ -15,34 +15,28 @@ impl<const LANES: usize> LaneCount<LANES> {
 pub trait SupportedLaneCount: Sealed {
     #[doc(hidden)]
     type BitMask: Copy + Default + AsRef<[u8]> + AsMut<[u8]>;
-
-    #[doc(hidden)]
-    type IntBitMask;
 }
 
 impl<const LANES: usize> Sealed for LaneCount<LANES> {}
 
 impl SupportedLaneCount for LaneCount<1> {
     type BitMask = [u8; 1];
-    type IntBitMask = u8;
 }
 impl SupportedLaneCount for LaneCount<2> {
     type BitMask = [u8; 1];
-    type IntBitMask = u8;
 }
 impl SupportedLaneCount for LaneCount<4> {
     type BitMask = [u8; 1];
-    type IntBitMask = u8;
 }
 impl SupportedLaneCount for LaneCount<8> {
     type BitMask = [u8; 1];
-    type IntBitMask = u8;
 }
 impl SupportedLaneCount for LaneCount<16> {
     type BitMask = [u8; 2];
-    type IntBitMask = u16;
 }
 impl SupportedLaneCount for LaneCount<32> {
     type BitMask = [u8; 4];
-    type IntBitMask = u32;
+}
+impl SupportedLaneCount for LaneCount<64> {
+    type BitMask = [u8; 8];
 }
index d460da0d04f86692d968e6df81645b2e87a901f9..191e96903133f2ad9fb164266cb1de6d56f29ec9 100644 (file)
@@ -129,6 +129,7 @@ pub fn splat(value: bool) -> Self {
     /// # Safety
     /// All lanes must be either 0 or -1.
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub unsafe fn from_int_unchecked(value: Simd<T, LANES>) -> Self {
         unsafe { Self(mask_impl::Mask::from_int_unchecked(value)) }
     }
@@ -139,6 +140,7 @@ pub unsafe fn from_int_unchecked(value: Simd<T, LANES>) -> Self {
     /// # Panics
     /// Panics if any lane is not 0 or -1.
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub fn from_int(value: Simd<T, LANES>) -> Self {
         assert!(T::valid(value), "all values must be either 0 or -1",);
         unsafe { Self::from_int_unchecked(value) }
@@ -147,6 +149,7 @@ pub fn from_int(value: Simd<T, LANES>) -> Self {
     /// Converts the mask to a vector of integers, where 0 represents `false` and -1
     /// represents `true`.
     #[inline]
+    #[must_use = "method returns a new vector and does not mutate the original value"]
     pub fn to_int(self) -> Simd<T, LANES> {
         self.0.to_int()
     }
@@ -156,6 +159,7 @@ pub fn to_int(self) -> Simd<T, LANES> {
     /// # Safety
     /// `lane` must be less than `LANES`.
     #[inline]
+    #[must_use = "method returns a new bool and does not mutate the original value"]
     pub unsafe fn test_unchecked(&self, lane: usize) -> bool {
         unsafe { self.0.test_unchecked(lane) }
     }
@@ -165,6 +169,7 @@ pub unsafe fn test_unchecked(&self, lane: usize) -> bool {
     /// # Panics
     /// Panics if `lane` is greater than or equal to the number of lanes in the vector.
     #[inline]
+    #[must_use = "method returns a new bool and does not mutate the original value"]
     pub fn test(&self, lane: usize) -> bool {
         assert!(lane < LANES, "lane index out of range");
         unsafe { self.test_unchecked(lane) }
@@ -195,24 +200,30 @@ pub fn set(&mut self, lane: usize, value: bool) {
 
     /// Convert this mask to a bitmask, with one bit set per lane.
     #[cfg(feature = "generic_const_exprs")]
+    #[inline]
+    #[must_use = "method returns a new array and does not mutate the original value"]
     pub fn to_bitmask(self) -> [u8; LaneCount::<LANES>::BITMASK_LEN] {
         self.0.to_bitmask()
     }
 
     /// Convert a bitmask to a mask.
     #[cfg(feature = "generic_const_exprs")]
+    #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub fn from_bitmask(bitmask: [u8; LaneCount::<LANES>::BITMASK_LEN]) -> Self {
         Self(mask_impl::Mask::from_bitmask(bitmask))
     }
 
     /// Returns true if any lane is set, or false otherwise.
     #[inline]
+    #[must_use = "method returns a new bool and does not mutate the original value"]
     pub fn any(self) -> bool {
         self.0.any()
     }
 
     /// Returns true if all lanes are set, or false otherwise.
     #[inline]
+    #[must_use = "method returns a new bool and does not mutate the original value"]
     pub fn all(self) -> bool {
         self.0.all()
     }
@@ -245,6 +256,7 @@ impl<T, const LANES: usize> Default for Mask<T, LANES>
     LaneCount<LANES>: SupportedLaneCount,
 {
     #[inline]
+    #[must_use = "method returns a defaulted mask with all lanes set to false (0)"]
     fn default() -> Self {
         Self::splat(false)
     }
@@ -256,6 +268,7 @@ impl<T, const LANES: usize> PartialEq for Mask<T, LANES>
     LaneCount<LANES>: SupportedLaneCount,
 {
     #[inline]
+    #[must_use = "method returns a new bool and does not mutate the original value"]
     fn eq(&self, other: &Self) -> bool {
         self.0 == other.0
     }
@@ -267,6 +280,7 @@ impl<T, const LANES: usize> PartialOrd for Mask<T, LANES>
     LaneCount<LANES>: SupportedLaneCount,
 {
     #[inline]
+    #[must_use = "method returns a new Ordering and does not mutate the original value"]
     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
         self.0.partial_cmp(&other.0)
     }
@@ -291,6 +305,7 @@ impl<T, const LANES: usize> core::ops::BitAnd for Mask<T, LANES>
 {
     type Output = Self;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitand(self, rhs: Self) -> Self {
         Self(self.0 & rhs.0)
     }
@@ -303,6 +318,7 @@ impl<T, const LANES: usize> core::ops::BitAnd<bool> for Mask<T, LANES>
 {
     type Output = Self;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitand(self, rhs: bool) -> Self {
         self & Self::splat(rhs)
     }
@@ -315,6 +331,7 @@ impl<T, const LANES: usize> core::ops::BitAnd<Mask<T, LANES>> for bool
 {
     type Output = Mask<T, LANES>;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitand(self, rhs: Mask<T, LANES>) -> Mask<T, LANES> {
         Mask::splat(self) & rhs
     }
@@ -327,6 +344,7 @@ impl<T, const LANES: usize> core::ops::BitOr for Mask<T, LANES>
 {
     type Output = Self;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitor(self, rhs: Self) -> Self {
         Self(self.0 | rhs.0)
     }
@@ -339,6 +357,7 @@ impl<T, const LANES: usize> core::ops::BitOr<bool> for Mask<T, LANES>
 {
     type Output = Self;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitor(self, rhs: bool) -> Self {
         self | Self::splat(rhs)
     }
@@ -351,6 +370,7 @@ impl<T, const LANES: usize> core::ops::BitOr<Mask<T, LANES>> for bool
 {
     type Output = Mask<T, LANES>;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitor(self, rhs: Mask<T, LANES>) -> Mask<T, LANES> {
         Mask::splat(self) | rhs
     }
@@ -363,6 +383,7 @@ impl<T, const LANES: usize> core::ops::BitXor for Mask<T, LANES>
 {
     type Output = Self;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitxor(self, rhs: Self) -> Self::Output {
         Self(self.0 ^ rhs.0)
     }
@@ -375,6 +396,7 @@ impl<T, const LANES: usize> core::ops::BitXor<bool> for Mask<T, LANES>
 {
     type Output = Self;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitxor(self, rhs: bool) -> Self::Output {
         self ^ Self::splat(rhs)
     }
@@ -387,6 +409,7 @@ impl<T, const LANES: usize> core::ops::BitXor<Mask<T, LANES>> for bool
 {
     type Output = Mask<T, LANES>;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitxor(self, rhs: Mask<T, LANES>) -> Self::Output {
         Mask::splat(self) ^ rhs
     }
@@ -399,6 +422,7 @@ impl<T, const LANES: usize> core::ops::Not for Mask<T, LANES>
 {
     type Output = Mask<T, LANES>;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn not(self) -> Self::Output {
         Self(!self.0)
     }
index 2689e1a88a8c4b4256d0262244c0b6170489778b..4c964cb52e1963851689fc06ef70c39dc5fcdb9d 100644 (file)
@@ -1,3 +1,4 @@
+#![allow(unused_imports)]
 use super::MaskElement;
 use crate::simd::intrinsics;
 use crate::simd::{LaneCount, Simd, SupportedLaneCount};
@@ -73,6 +74,7 @@ impl<T, const LANES: usize> Mask<T, LANES>
     LaneCount<LANES>: SupportedLaneCount,
 {
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub fn splat(value: bool) -> Self {
         let mut mask = <LaneCount<LANES> as SupportedLaneCount>::BitMask::default();
         if value {
@@ -87,6 +89,7 @@ pub fn splat(value: bool) -> Self {
     }
 
     #[inline]
+    #[must_use = "method returns a new bool and does not mutate the original value"]
     pub unsafe fn test_unchecked(&self, lane: usize) -> bool {
         (self.0.as_ref()[lane / 8] >> (lane % 8)) & 0x1 > 0
     }
@@ -99,30 +102,26 @@ pub unsafe fn set_unchecked(&mut self, lane: usize, value: bool) {
     }
 
     #[inline]
+    #[must_use = "method returns a new vector and does not mutate the original value"]
     pub fn to_int(self) -> Simd<T, LANES> {
         unsafe {
-            let mask: <LaneCount<LANES> as SupportedLaneCount>::IntBitMask =
-                core::mem::transmute_copy(&self);
-            intrinsics::simd_select_bitmask(mask, Simd::splat(T::TRUE), Simd::splat(T::FALSE))
+            crate::intrinsics::simd_select_bitmask(
+                self.0,
+                Simd::splat(T::TRUE),
+                Simd::splat(T::FALSE),
+            )
         }
     }
 
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub unsafe fn from_int_unchecked(value: Simd<T, LANES>) -> Self {
-        // TODO remove the transmute when rustc is more flexible
-        assert_eq!(
-            core::mem::size_of::<<LaneCount::<LANES> as SupportedLaneCount>::BitMask>(),
-            core::mem::size_of::<<LaneCount::<LANES> as SupportedLaneCount>::IntBitMask>(),
-        );
-        unsafe {
-            let mask: <LaneCount<LANES> as SupportedLaneCount>::IntBitMask =
-                intrinsics::simd_bitmask(value);
-            Self(core::mem::transmute_copy(&mask), PhantomData)
-        }
+        unsafe { Self(crate::intrinsics::simd_bitmask(value), PhantomData) }
     }
 
     #[cfg(feature = "generic_const_exprs")]
     #[inline]
+    #[must_use = "method returns a new array and does not mutate the original value"]
     pub fn to_bitmask(self) -> [u8; LaneCount::<LANES>::BITMASK_LEN] {
         // Safety: these are the same type and we are laundering the generic
         unsafe { core::mem::transmute_copy(&self.0) }
@@ -130,12 +129,14 @@ pub unsafe fn from_int_unchecked(value: Simd<T, LANES>) -> Self {
 
     #[cfg(feature = "generic_const_exprs")]
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub fn from_bitmask(bitmask: [u8; LaneCount::<LANES>::BITMASK_LEN]) -> Self {
         // Safety: these are the same type and we are laundering the generic
         Self(unsafe { core::mem::transmute_copy(&bitmask) }, PhantomData)
     }
 
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub fn convert<U>(self) -> Mask<U, LANES>
     where
         U: MaskElement,
@@ -144,11 +145,13 @@ pub fn convert<U>(self) -> Mask<U, LANES>
     }
 
     #[inline]
+    #[must_use = "method returns a new bool and does not mutate the original value"]
     pub fn any(self) -> bool {
         self != Self::splat(false)
     }
 
     #[inline]
+    #[must_use = "method returns a new bool and does not mutate the original value"]
     pub fn all(self) -> bool {
         self == Self::splat(true)
     }
@@ -162,6 +165,7 @@ impl<T, const LANES: usize> core::ops::BitAnd for Mask<T, LANES>
 {
     type Output = Self;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitand(mut self, rhs: Self) -> Self {
         for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) {
             *l &= r;
@@ -178,6 +182,7 @@ impl<T, const LANES: usize> core::ops::BitOr for Mask<T, LANES>
 {
     type Output = Self;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitor(mut self, rhs: Self) -> Self {
         for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) {
             *l |= r;
@@ -193,6 +198,7 @@ impl<T, const LANES: usize> core::ops::BitXor for Mask<T, LANES>
 {
     type Output = Self;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitxor(mut self, rhs: Self) -> Self::Output {
         for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) {
             *l ^= r;
@@ -208,6 +214,7 @@ impl<T, const LANES: usize> core::ops::Not for Mask<T, LANES>
 {
     type Output = Self;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn not(mut self) -> Self::Output {
         for x in self.0.as_mut() {
             *x = !*x;
index dd981cedb932b4c57681caebb9282a012780a375..5421ccbe3d8f5ce9c5e3b7221609314990412fcf 100644 (file)
@@ -23,6 +23,7 @@ impl<T, const LANES: usize> Clone for Mask<T, LANES>
     LaneCount<LANES>: SupportedLaneCount,
 {
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn clone(&self) -> Self {
         *self
     }
@@ -70,11 +71,14 @@ impl<T, const LANES: usize> Mask<T, LANES>
     T: MaskElement,
     LaneCount<LANES>: SupportedLaneCount,
 {
+    #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub fn splat(value: bool) -> Self {
         Self(Simd::splat(if value { T::TRUE } else { T::FALSE }))
     }
 
     #[inline]
+    #[must_use = "method returns a new bool and does not mutate the original value"]
     pub unsafe fn test_unchecked(&self, lane: usize) -> bool {
         T::eq(self.0[lane], T::TRUE)
     }
@@ -85,16 +89,19 @@ pub unsafe fn set_unchecked(&mut self, lane: usize, value: bool) {
     }
 
     #[inline]
+    #[must_use = "method returns a new vector and does not mutate the original value"]
     pub fn to_int(self) -> Simd<T, LANES> {
         self.0
     }
 
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub unsafe fn from_int_unchecked(value: Simd<T, LANES>) -> Self {
         Self(value)
     }
 
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub fn convert<U>(self) -> Mask<U, LANES>
     where
         U: MaskElement,
@@ -104,17 +111,11 @@ pub fn convert<U>(self) -> Mask<U, LANES>
 
     #[cfg(feature = "generic_const_exprs")]
     #[inline]
+    #[must_use = "method returns a new array and does not mutate the original value"]
     pub fn to_bitmask(self) -> [u8; LaneCount::<LANES>::BITMASK_LEN] {
         unsafe {
-            // TODO remove the transmute when rustc can use arrays of u8 as bitmasks
-            assert_eq!(
-                core::mem::size_of::<<LaneCount::<LANES> as SupportedLaneCount>::IntBitMask>(),
-                LaneCount::<LANES>::BITMASK_LEN,
-            );
-            let bitmask: <LaneCount<LANES> as SupportedLaneCount>::IntBitMask =
-                intrinsics::simd_bitmask(self.0);
             let mut bitmask: [u8; LaneCount::<LANES>::BITMASK_LEN] =
-                core::mem::transmute_copy(&bitmask);
+                crate::intrinsics::simd_bitmask(self.0);
 
             // There is a bug where LLVM appears to implement this operation with the wrong
             // bit order.
@@ -131,6 +132,7 @@ pub fn convert<U>(self) -> Mask<U, LANES>
 
     #[cfg(feature = "generic_const_exprs")]
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     pub fn from_bitmask(mut bitmask: [u8; LaneCount::<LANES>::BITMASK_LEN]) -> Self {
         unsafe {
             // There is a bug where LLVM appears to implement this operation with the wrong
@@ -142,15 +144,7 @@ pub fn convert<U>(self) -> Mask<U, LANES>
                 }
             }
 
-            // TODO remove the transmute when rustc can use arrays of u8 as bitmasks
-            assert_eq!(
-                core::mem::size_of::<<LaneCount::<LANES> as SupportedLaneCount>::IntBitMask>(),
-                LaneCount::<LANES>::BITMASK_LEN,
-            );
-            let bitmask: <LaneCount<LANES> as SupportedLaneCount>::IntBitMask =
-                core::mem::transmute_copy(&bitmask);
-
-            Self::from_int_unchecked(intrinsics::simd_select_bitmask(
+            Self::from_int_unchecked(crate::intrinsics::simd_select_bitmask(
                 bitmask,
                 Self::splat(true).to_int(),
                 Self::splat(false).to_int(),
@@ -159,11 +153,13 @@ pub fn convert<U>(self) -> Mask<U, LANES>
     }
 
     #[inline]
+    #[must_use = "method returns a new bool and does not mutate the original value"]
     pub fn any(self) -> bool {
         unsafe { intrinsics::simd_reduce_any(self.to_int()) }
     }
 
     #[inline]
+    #[must_use = "method returns a new vector and does not mutate the original value"]
     pub fn all(self) -> bool {
         unsafe { intrinsics::simd_reduce_all(self.to_int()) }
     }
@@ -186,6 +182,7 @@ impl<T, const LANES: usize> core::ops::BitAnd for Mask<T, LANES>
 {
     type Output = Self;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitand(self, rhs: Self) -> Self {
         unsafe { Self(intrinsics::simd_and(self.0, rhs.0)) }
     }
@@ -198,6 +195,7 @@ impl<T, const LANES: usize> core::ops::BitOr for Mask<T, LANES>
 {
     type Output = Self;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitor(self, rhs: Self) -> Self {
         unsafe { Self(intrinsics::simd_or(self.0, rhs.0)) }
     }
@@ -210,6 +208,7 @@ impl<T, const LANES: usize> core::ops::BitXor for Mask<T, LANES>
 {
     type Output = Self;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn bitxor(self, rhs: Self) -> Self {
         unsafe { Self(intrinsics::simd_xor(self.0, rhs.0)) }
     }
@@ -222,6 +221,7 @@ impl<T, const LANES: usize> core::ops::Not for Mask<T, LANES>
 {
     type Output = Self;
     #[inline]
+    #[must_use = "method returns a new mask and does not mutate the original value"]
     fn not(self) -> Self::Output {
         Self::splat(true) ^ self
     }
index 2bae414ebfb5494621051e09554287683b3e10a7..7435b6df9186098a4e1ce5c3b4f8b61ae0a8316b 100644 (file)
@@ -17,7 +17,7 @@ macro_rules! impl_uint_arith {
             /// let max = Simd::splat(MAX);
             /// let unsat = x + max;
             /// let sat = x.saturating_add(max);
-            /// assert_eq!(x - 1, unsat);
+            /// assert_eq!(unsat, Simd::from_array([1, 0, MAX, MAX - 1]));
             /// assert_eq!(sat, max);
             /// ```
             #[inline]
@@ -37,7 +37,7 @@ pub fn saturating_add(self, second: Self) -> Self {
             /// let max = Simd::splat(MAX);
             /// let unsat = x - max;
             /// let sat = x.saturating_sub(max);
-            /// assert_eq!(unsat, x + 1);
+            /// assert_eq!(unsat, Simd::from_array([3, 2, 1, 0]));
             /// assert_eq!(sat, Simd::splat(0));
             #[inline]
             pub fn saturating_sub(self, second: Self) -> Self {
@@ -105,7 +105,7 @@ pub fn saturating_sub(self, second: Self) -> Self {
             #[inline]
             pub fn abs(self) -> Self {
                 const SHR: $ty = <$ty>::BITS as $ty - 1;
-                let m = self >> SHR;
+                let m = self >> Simd::splat(SHR);
                 (self^m) - m
             }
 
@@ -128,7 +128,7 @@ pub fn abs(self) -> Self {
             pub fn saturating_abs(self) -> Self {
                 // arith shift for -1 or 0 mask based on sign bit, giving 2s complement
                 const SHR: $ty = <$ty>::BITS as $ty - 1;
-                let m = self >> SHR;
+                let m = self >> Simd::splat(SHR);
                 (self^m).saturating_sub(m)
             }
 
index 5d7af474caf7005c9045bcb5e9cc99b226ae7872..3582c57870b9e83f0420642dc67865d6ed08ec8a 100644 (file)
@@ -1,5 +1,13 @@
 use crate::simd::intrinsics;
 use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
+use core::ops::{Add, Mul};
+use core::ops::{BitAnd, BitOr, BitXor};
+use core::ops::{Div, Rem, Sub};
+use core::ops::{Shl, Shr};
+
+mod assign;
+mod deref;
+mod unary;
 
 impl<I, T, const LANES: usize> core::ops::Index<I> for Simd<T, LANES>
 where
@@ -57,166 +65,44 @@ impl<const $lanes: usize> core::ops::$trait<$rhs> for $type
             $(#[$attrs])*
             fn $fn($self_tok, $rhs_arg: $rhs_arg_ty) -> Self::Output $body
         }
-
-        impl<const $lanes: usize> core::ops::$trait<&'_ $rhs> for $type
-        where
-            LaneCount<$lanes2>: SupportedLaneCount,
-        {
-            type Output = <$type as core::ops::$trait<$rhs>>::Output;
-
-            $(#[$attrs])*
-            fn $fn($self_tok, $rhs_arg: &$rhs) -> Self::Output {
-                core::ops::$trait::$fn($self_tok, *$rhs_arg)
-            }
-        }
-
-        impl<const $lanes: usize> core::ops::$trait<$rhs> for &'_ $type
-        where
-            LaneCount<$lanes2>: SupportedLaneCount,
-        {
-            type Output = <$type as core::ops::$trait<$rhs>>::Output;
-
-            $(#[$attrs])*
-            fn $fn($self_tok, $rhs_arg: $rhs) -> Self::Output {
-                core::ops::$trait::$fn(*$self_tok, $rhs_arg)
-            }
-        }
-
-        impl<const $lanes: usize> core::ops::$trait<&'_ $rhs> for &'_ $type
-        where
-            LaneCount<$lanes2>: SupportedLaneCount,
-        {
-            type Output = <$type as core::ops::$trait<$rhs>>::Output;
-
-            $(#[$attrs])*
-            fn $fn($self_tok, $rhs_arg: &$rhs) -> Self::Output {
-                core::ops::$trait::$fn(*$self_tok, *$rhs_arg)
-            }
-        }
     };
-
-    // binary assignment op
-    {
-        impl<const $lanes:ident: usize> core::ops::$trait:ident<$rhs:ty> for $type:ty
-        where
-            LaneCount<$lanes2:ident>: SupportedLaneCount,
-        {
-            $(#[$attrs:meta])*
-            fn $fn:ident(&mut $self_tok:ident, $rhs_arg:ident: $rhs_arg_ty:ty) $body:tt
-        }
-    } => {
-        impl<const $lanes: usize> core::ops::$trait<$rhs> for $type
-        where
-            LaneCount<$lanes2>: SupportedLaneCount,
-        {
-            $(#[$attrs])*
-            fn $fn(&mut $self_tok, $rhs_arg: $rhs_arg_ty) $body
-        }
-
-        impl<const $lanes: usize> core::ops::$trait<&'_ $rhs> for $type
-        where
-            LaneCount<$lanes2>: SupportedLaneCount,
-        {
-            $(#[$attrs])*
-            fn $fn(&mut $self_tok, $rhs_arg: &$rhs_arg_ty) {
-                core::ops::$trait::$fn($self_tok, *$rhs_arg)
-            }
-        }
-    };
-
-    // unary op
-    {
-        impl<const $lanes:ident: usize> core::ops::$trait:ident for $type:ty
-        where
-            LaneCount<$lanes2:ident>: SupportedLaneCount,
-        {
-            type Output = $output:ty;
-            fn $fn:ident($self_tok:ident) -> Self::Output $body:tt
-        }
-    } => {
-        impl<const $lanes: usize> core::ops::$trait for $type
-        where
-            LaneCount<$lanes2>: SupportedLaneCount,
-        {
-            type Output = $output;
-            fn $fn($self_tok) -> Self::Output $body
-        }
-
-        impl<const $lanes: usize> core::ops::$trait for &'_ $type
-        where
-            LaneCount<$lanes2>: SupportedLaneCount,
-        {
-            type Output = <$type as core::ops::$trait>::Output;
-            fn $fn($self_tok) -> Self::Output {
-                core::ops::$trait::$fn(*$self_tok)
-            }
-        }
-    }
 }
 
 /// Automatically implements operators over vectors and scalars for a particular vector.
 macro_rules! impl_op {
     { impl Add for $scalar:ty } => {
-        impl_op! { @binary $scalar, Add::add, AddAssign::add_assign, simd_add }
+        impl_op! { @binary $scalar, Add::add, simd_add }
     };
     { impl Sub for $scalar:ty } => {
-        impl_op! { @binary $scalar, Sub::sub, SubAssign::sub_assign, simd_sub }
+        impl_op! { @binary $scalar, Sub::sub, simd_sub }
     };
     { impl Mul for $scalar:ty } => {
-        impl_op! { @binary $scalar, Mul::mul, MulAssign::mul_assign, simd_mul }
+        impl_op! { @binary $scalar, Mul::mul, simd_mul }
     };
     { impl Div for $scalar:ty } => {
-        impl_op! { @binary $scalar, Div::div, DivAssign::div_assign, simd_div }
+        impl_op! { @binary $scalar, Div::div, simd_div }
     };
     { impl Rem for $scalar:ty } => {
-        impl_op! { @binary $scalar, Rem::rem, RemAssign::rem_assign, simd_rem }
+        impl_op! { @binary $scalar, Rem::rem, simd_rem }
     };
     { impl Shl for $scalar:ty } => {
-        impl_op! { @binary $scalar, Shl::shl, ShlAssign::shl_assign, simd_shl }
+        impl_op! { @binary $scalar, Shl::shl, simd_shl }
     };
     { impl Shr for $scalar:ty } => {
-        impl_op! { @binary $scalar, Shr::shr, ShrAssign::shr_assign, simd_shr }
+        impl_op! { @binary $scalar, Shr::shr, simd_shr }
     };
     { impl BitAnd for $scalar:ty } => {
-        impl_op! { @binary $scalar, BitAnd::bitand, BitAndAssign::bitand_assign, simd_and }
+        impl_op! { @binary $scalar, BitAnd::bitand, simd_and }
     };
     { impl BitOr for $scalar:ty } => {
-        impl_op! { @binary $scalar, BitOr::bitor, BitOrAssign::bitor_assign, simd_or }
+        impl_op! { @binary $scalar, BitOr::bitor, simd_or }
     };
     { impl BitXor for $scalar:ty } => {
-        impl_op! { @binary $scalar, BitXor::bitxor, BitXorAssign::bitxor_assign, simd_xor }
-    };
-
-    { impl Not for $scalar:ty } => {
-        impl_ref_ops! {
-            impl<const LANES: usize> core::ops::Not for Simd<$scalar, LANES>
-            where
-                LaneCount<LANES>: SupportedLaneCount,
-            {
-                type Output = Self;
-                fn not(self) -> Self::Output {
-                    self ^ Self::splat(!<$scalar>::default())
-                }
-            }
-        }
-    };
-
-    { impl Neg for $scalar:ty } => {
-        impl_ref_ops! {
-            impl<const LANES: usize> core::ops::Neg for Simd<$scalar, LANES>
-            where
-                LaneCount<LANES>: SupportedLaneCount,
-            {
-                type Output = Self;
-                fn neg(self) -> Self::Output {
-                    unsafe { intrinsics::simd_neg(self) }
-                }
-            }
-        }
+        impl_op! { @binary $scalar, BitXor::bitxor, simd_xor }
     };
 
     // generic binary op with assignment when output is `Self`
-    { @binary $scalar:ty, $trait:ident :: $trait_fn:ident, $assign_trait:ident :: $assign_trait_fn:ident, $intrinsic:ident } => {
+    { @binary $scalar:ty, $trait:ident :: $trait_fn:ident, $intrinsic:ident } => {
         impl_ref_ops! {
             impl<const LANES: usize> core::ops::$trait<Self> for Simd<$scalar, LANES>
             where
@@ -232,60 +118,6 @@ fn $trait_fn(self, rhs: Self) -> Self::Output {
                 }
             }
         }
-
-        impl_ref_ops! {
-            impl<const LANES: usize> core::ops::$trait<$scalar> for Simd<$scalar, LANES>
-            where
-                LaneCount<LANES>: SupportedLaneCount,
-            {
-                type Output = Self;
-
-                #[inline]
-                fn $trait_fn(self, rhs: $scalar) -> Self::Output {
-                    core::ops::$trait::$trait_fn(self, Self::splat(rhs))
-                }
-            }
-        }
-
-        impl_ref_ops! {
-            impl<const LANES: usize> core::ops::$trait<Simd<$scalar, LANES>> for $scalar
-            where
-                LaneCount<LANES>: SupportedLaneCount,
-            {
-                type Output = Simd<$scalar, LANES>;
-
-                #[inline]
-                fn $trait_fn(self, rhs: Simd<$scalar, LANES>) -> Self::Output {
-                    core::ops::$trait::$trait_fn(Simd::splat(self), rhs)
-                }
-            }
-        }
-
-        impl_ref_ops! {
-            impl<const LANES: usize> core::ops::$assign_trait<Self> for Simd<$scalar, LANES>
-            where
-                LaneCount<LANES>: SupportedLaneCount,
-            {
-                #[inline]
-                fn $assign_trait_fn(&mut self, rhs: Self) {
-                    unsafe {
-                        *self = intrinsics::$intrinsic(*self, rhs);
-                    }
-                }
-            }
-        }
-
-        impl_ref_ops! {
-            impl<const LANES: usize> core::ops::$assign_trait<$scalar> for Simd<$scalar, LANES>
-            where
-                LaneCount<LANES>: SupportedLaneCount,
-            {
-                #[inline]
-                fn $assign_trait_fn(&mut self, rhs: $scalar) {
-                    core::ops::$assign_trait::$assign_trait_fn(self, Self::splat(rhs));
-                }
-            }
-        }
     };
 }
 
@@ -298,7 +130,6 @@ macro_rules! impl_float_ops {
             impl_op! { impl Mul for $scalar }
             impl_op! { impl Div for $scalar }
             impl_op! { impl Rem for $scalar }
-            impl_op! { impl Neg for $scalar }
         )*
     };
 }
@@ -313,7 +144,6 @@ macro_rules! impl_unsigned_int_ops {
             impl_op! { impl BitAnd for $scalar }
             impl_op! { impl BitOr  for $scalar }
             impl_op! { impl BitXor for $scalar }
-            impl_op! { impl Not for $scalar }
 
             // Integers panic on divide by 0
             impl_ref_ops! {
@@ -344,67 +174,6 @@ fn div(self, rhs: Self) -> Self::Output {
                 }
             }
 
-            impl_ref_ops! {
-                impl<const LANES: usize> core::ops::Div<$scalar> for Simd<$scalar, LANES>
-                where
-                    LaneCount<LANES>: SupportedLaneCount,
-                {
-                    type Output = Self;
-
-                    #[inline]
-                    fn div(self, rhs: $scalar) -> Self::Output {
-                        if rhs == 0 {
-                            panic!("attempt to divide by zero");
-                        }
-                        if <$scalar>::MIN != 0 &&
-                            self.as_array().iter().any(|x| *x == <$scalar>::MIN) &&
-                            rhs == -1 as _ {
-                                panic!("attempt to divide with overflow");
-                        }
-                        let rhs = Self::splat(rhs);
-                        unsafe { intrinsics::simd_div(self, rhs) }
-                    }
-                }
-            }
-
-            impl_ref_ops! {
-                impl<const LANES: usize> core::ops::Div<Simd<$scalar, LANES>> for $scalar
-                where
-                    LaneCount<LANES>: SupportedLaneCount,
-                {
-                    type Output = Simd<$scalar, LANES>;
-
-                    #[inline]
-                    fn div(self, rhs: Simd<$scalar, LANES>) -> Self::Output {
-                        Simd::splat(self) / rhs
-                    }
-                }
-            }
-
-            impl_ref_ops! {
-                impl<const LANES: usize> core::ops::DivAssign<Self> for Simd<$scalar, LANES>
-                where
-                    LaneCount<LANES>: SupportedLaneCount,
-                {
-                    #[inline]
-                    fn div_assign(&mut self, rhs: Self) {
-                        *self = *self / rhs;
-                    }
-                }
-            }
-
-            impl_ref_ops! {
-                impl<const LANES: usize> core::ops::DivAssign<$scalar> for Simd<$scalar, LANES>
-                where
-                    LaneCount<LANES>: SupportedLaneCount,
-                {
-                    #[inline]
-                    fn div_assign(&mut self, rhs: $scalar) {
-                        *self = *self / rhs;
-                    }
-                }
-            }
-
             // remainder panics on zero divisor
             impl_ref_ops! {
                 impl<const LANES: usize> core::ops::Rem<Self> for Simd<$scalar, LANES>
@@ -434,67 +203,6 @@ fn rem(self, rhs: Self) -> Self::Output {
                 }
             }
 
-            impl_ref_ops! {
-                impl<const LANES: usize> core::ops::Rem<$scalar> for Simd<$scalar, LANES>
-                where
-                    LaneCount<LANES>: SupportedLaneCount,
-                {
-                    type Output = Self;
-
-                    #[inline]
-                    fn rem(self, rhs: $scalar) -> Self::Output {
-                        if rhs == 0 {
-                            panic!("attempt to calculate the remainder with a divisor of zero");
-                        }
-                        if <$scalar>::MIN != 0 &&
-                            self.as_array().iter().any(|x| *x == <$scalar>::MIN) &&
-                            rhs == -1 as _ {
-                                panic!("attempt to calculate the remainder with overflow");
-                        }
-                        let rhs = Self::splat(rhs);
-                        unsafe { intrinsics::simd_rem(self, rhs) }
-                    }
-                }
-            }
-
-            impl_ref_ops! {
-                impl<const LANES: usize> core::ops::Rem<Simd<$scalar, LANES>> for $scalar
-                where
-                    LaneCount<LANES>: SupportedLaneCount,
-                {
-                    type Output = Simd<$scalar, LANES>;
-
-                    #[inline]
-                    fn rem(self, rhs: Simd<$scalar, LANES>) -> Self::Output {
-                        Simd::splat(self) % rhs
-                    }
-                }
-            }
-
-            impl_ref_ops! {
-                impl<const LANES: usize> core::ops::RemAssign<Self> for Simd<$scalar, LANES>
-                where
-                    LaneCount<LANES>: SupportedLaneCount,
-                {
-                    #[inline]
-                    fn rem_assign(&mut self, rhs: Self) {
-                        *self = *self % rhs;
-                    }
-                }
-            }
-
-            impl_ref_ops! {
-                impl<const LANES: usize> core::ops::RemAssign<$scalar> for Simd<$scalar, LANES>
-                where
-                    LaneCount<LANES>: SupportedLaneCount,
-                {
-                    #[inline]
-                    fn rem_assign(&mut self, rhs: $scalar) {
-                        *self = *self % rhs;
-                    }
-                }
-            }
-
             // shifts panic on overflow
             impl_ref_ops! {
                 impl<const LANES: usize> core::ops::Shl<Self> for Simd<$scalar, LANES>
@@ -518,49 +226,6 @@ fn shl(self, rhs: Self) -> Self::Output {
                 }
             }
 
-            impl_ref_ops! {
-                impl<const LANES: usize> core::ops::Shl<$scalar> for Simd<$scalar, LANES>
-                where
-                    LaneCount<LANES>: SupportedLaneCount,
-                {
-                    type Output = Self;
-
-                    #[inline]
-                    fn shl(self, rhs: $scalar) -> Self::Output {
-                        if invalid_shift_rhs(rhs) {
-                            panic!("attempt to shift left with overflow");
-                        }
-                        let rhs = Self::splat(rhs);
-                        unsafe { intrinsics::simd_shl(self, rhs) }
-                    }
-                }
-            }
-
-
-            impl_ref_ops! {
-                impl<const LANES: usize> core::ops::ShlAssign<Self> for Simd<$scalar, LANES>
-                where
-                    LaneCount<LANES>: SupportedLaneCount,
-                {
-                    #[inline]
-                    fn shl_assign(&mut self, rhs: Self) {
-                        *self = *self << rhs;
-                    }
-                }
-            }
-
-            impl_ref_ops! {
-                impl<const LANES: usize> core::ops::ShlAssign<$scalar> for Simd<$scalar, LANES>
-                where
-                    LaneCount<LANES>: SupportedLaneCount,
-                {
-                    #[inline]
-                    fn shl_assign(&mut self, rhs: $scalar) {
-                        *self = *self << rhs;
-                    }
-                }
-            }
-
             impl_ref_ops! {
                 impl<const LANES: usize> core::ops::Shr<Self> for Simd<$scalar, LANES>
                 where
@@ -582,49 +247,6 @@ fn shr(self, rhs: Self) -> Self::Output {
                     }
                 }
             }
-
-            impl_ref_ops! {
-                impl<const LANES: usize> core::ops::Shr<$scalar> for Simd<$scalar, LANES>
-                where
-                    LaneCount<LANES>: SupportedLaneCount,
-                {
-                    type Output = Self;
-
-                    #[inline]
-                    fn shr(self, rhs: $scalar) -> Self::Output {
-                        if invalid_shift_rhs(rhs) {
-                            panic!("attempt to shift with overflow");
-                        }
-                        let rhs = Self::splat(rhs);
-                        unsafe { intrinsics::simd_shr(self, rhs) }
-                    }
-                }
-            }
-
-
-            impl_ref_ops! {
-                impl<const LANES: usize> core::ops::ShrAssign<Self> for Simd<$scalar, LANES>
-                where
-                    LaneCount<LANES>: SupportedLaneCount,
-                {
-                    #[inline]
-                    fn shr_assign(&mut self, rhs: Self) {
-                        *self = *self >> rhs;
-                    }
-                }
-            }
-
-            impl_ref_ops! {
-                impl<const LANES: usize> core::ops::ShrAssign<$scalar> for Simd<$scalar, LANES>
-                where
-                    LaneCount<LANES>: SupportedLaneCount,
-                {
-                    #[inline]
-                    fn shr_assign(&mut self, rhs: $scalar) {
-                        *self = *self >> rhs;
-                    }
-                }
-            }
         )*
     };
 }
@@ -633,9 +255,6 @@ fn shr_assign(&mut self, rhs: $scalar) {
 macro_rules! impl_signed_int_ops {
     { $($scalar:ty),* } => {
         impl_unsigned_int_ops! { $($scalar),* }
-        $( // scalar
-            impl_op! { impl Neg for $scalar }
-        )*
     };
 }
 
diff --git a/library/portable-simd/crates/core_simd/src/ops/assign.rs b/library/portable-simd/crates/core_simd/src/ops/assign.rs
new file mode 100644 (file)
index 0000000..d2b4861
--- /dev/null
@@ -0,0 +1,124 @@
+//! Assignment operators
+use super::*;
+use core::ops::{AddAssign, MulAssign}; // commutative binary op-assignment
+use core::ops::{BitAndAssign, BitOrAssign, BitXorAssign}; // commutative bit binary op-assignment
+use core::ops::{DivAssign, RemAssign, SubAssign}; // non-commutative binary op-assignment
+use core::ops::{ShlAssign, ShrAssign}; // non-commutative bit binary op-assignment
+
+// Arithmetic
+
+macro_rules! assign_ops {
+    ($(impl<T, U, const LANES: usize> $assignTrait:ident<U> for Simd<T, LANES>
+        where
+            Self: $trait:ident,
+        {
+            fn $assign_call:ident(rhs: U) {
+                $call:ident
+            }
+        })*) => {
+        $(impl<T, U, const LANES: usize> $assignTrait<U> for Simd<T, LANES>
+        where
+            Self: $trait<U, Output = Self>,
+            T: SimdElement,
+            LaneCount<LANES>: SupportedLaneCount,
+        {
+            #[inline]
+            fn $assign_call(&mut self, rhs: U) {
+                *self = self.$call(rhs);
+            }
+        })*
+    }
+}
+
+assign_ops! {
+    // Arithmetic
+    impl<T, U, const LANES: usize> AddAssign<U> for Simd<T, LANES>
+    where
+        Self: Add,
+    {
+        fn add_assign(rhs: U) {
+            add
+        }
+    }
+
+    impl<T, U, const LANES: usize> MulAssign<U> for Simd<T, LANES>
+    where
+        Self: Mul,
+    {
+        fn mul_assign(rhs: U) {
+            mul
+        }
+    }
+
+    impl<T, U, const LANES: usize> SubAssign<U> for Simd<T, LANES>
+    where
+        Self: Sub,
+    {
+        fn sub_assign(rhs: U) {
+            sub
+        }
+    }
+
+    impl<T, U, const LANES: usize> DivAssign<U> for Simd<T, LANES>
+    where
+        Self: Div,
+    {
+        fn div_assign(rhs: U) {
+            div
+        }
+    }
+    impl<T, U, const LANES: usize> RemAssign<U> for Simd<T, LANES>
+    where
+        Self: Rem,
+    {
+        fn rem_assign(rhs: U) {
+            rem
+        }
+    }
+
+    // Bitops
+    impl<T, U, const LANES: usize> BitAndAssign<U> for Simd<T, LANES>
+    where
+        Self: BitAnd,
+    {
+        fn bitand_assign(rhs: U) {
+            bitand
+        }
+    }
+
+    impl<T, U, const LANES: usize> BitOrAssign<U> for Simd<T, LANES>
+    where
+        Self: BitOr,
+    {
+        fn bitor_assign(rhs: U) {
+            bitor
+        }
+    }
+
+    impl<T, U, const LANES: usize> BitXorAssign<U> for Simd<T, LANES>
+    where
+        Self: BitXor,
+    {
+        fn bitxor_assign(rhs: U) {
+            bitxor
+        }
+    }
+
+    impl<T, U, const LANES: usize> ShlAssign<U> for Simd<T, LANES>
+    where
+        Self: Shl,
+    {
+        fn shl_assign(rhs: U) {
+            shl
+        }
+    }
+
+    impl<T, U, const LANES: usize> ShrAssign<U> for Simd<T, LANES>
+    where
+        Self: Shr,
+    {
+        fn shr_assign(rhs: U) {
+            shr
+        }
+    }
+}
diff --git a/library/portable-simd/crates/core_simd/src/ops/deref.rs b/library/portable-simd/crates/core_simd/src/ops/deref.rs
new file mode 100644 (file)
index 0000000..9883a74
--- /dev/null
@@ -0,0 +1,124 @@
+//! This module hacks in "implicit deref" for Simd's operators.
+//! Ideally, Rust would take care of this itself,
+//! and method calls usually handle the LHS implicitly.
+//! But this is not the case with arithmetic ops.
+use super::*;
+
+macro_rules! deref_lhs {
+    (impl<T, const LANES: usize> $trait:ident for $simd:ty {
+            fn $call:ident
+        }) => {
+        impl<T, const LANES: usize> $trait<$simd> for &$simd
+        where
+            T: SimdElement,
+            $simd: $trait<$simd, Output = $simd>,
+            LaneCount<LANES>: SupportedLaneCount,
+        {
+            type Output = Simd<T, LANES>;
+
+            #[inline]
+            #[must_use = "operator returns a new vector without mutating the inputs"]
+            fn $call(self, rhs: $simd) -> Self::Output {
+                (*self).$call(rhs)
+            }
+        }
+    };
+}
+
+macro_rules! deref_rhs {
+    (impl<T, const LANES: usize> $trait:ident for $simd:ty {
+            fn $call:ident
+        }) => {
+        impl<T, const LANES: usize> $trait<&$simd> for $simd
+        where
+            T: SimdElement,
+            $simd: $trait<$simd, Output = $simd>,
+            LaneCount<LANES>: SupportedLaneCount,
+        {
+            type Output = Simd<T, LANES>;
+
+            #[inline]
+            #[must_use = "operator returns a new vector without mutating the inputs"]
+            fn $call(self, rhs: &$simd) -> Self::Output {
+                self.$call(*rhs)
+            }
+        }
+    };
+}
+
+macro_rules! deref_ops {
+    ($(impl<T, const LANES: usize> $trait:ident for $simd:ty {
+            fn $call:ident
+        })*) => {
+        $(
+            deref_rhs! {
+                impl<T, const LANES: usize> $trait for $simd {
+                    fn $call
+                }
+            }
+            deref_lhs! {
+                impl<T, const LANES: usize> $trait for $simd {
+                    fn $call
+                }
+            }
+            impl<'lhs, 'rhs, T, const LANES: usize> $trait<&'rhs $simd> for &'lhs $simd
+            where
+                T: SimdElement,
+                $simd: $trait<$simd, Output = $simd>,
+                LaneCount<LANES>: SupportedLaneCount,
+            {
+                type Output = $simd;
+
+                #[inline]
+                #[must_use = "operator returns a new vector without mutating the inputs"]
+                fn $call(self, rhs: &$simd) -> Self::Output {
+                    (*self).$call(*rhs)
+                }
+            }
+        )*
+    }
+}
+
+deref_ops! {
+    // Arithmetic
+    impl<T, const LANES: usize> Add for Simd<T, LANES> {
+        fn add
+    }
+
+    impl<T, const LANES: usize> Mul for Simd<T, LANES> {
+        fn mul
+    }
+
+    impl<T, const LANES: usize> Sub for Simd<T, LANES> {
+        fn sub
+    }
+
+    impl<T, const LANES: usize> Div for Simd<T, LANES> {
+        fn div
+    }
+
+    impl<T, const LANES: usize> Rem for Simd<T, LANES> {
+        fn rem
+    }
+
+    // Bitops
+    impl<T, const LANES: usize> BitAnd for Simd<T, LANES> {
+        fn bitand
+    }
+
+    impl<T, const LANES: usize> BitOr for Simd<T, LANES> {
+        fn bitor
+    }
+
+    impl<T, const LANES: usize> BitXor for Simd<T, LANES> {
+        fn bitxor
+    }
+
+    impl<T, const LANES: usize> Shl for Simd<T, LANES> {
+        fn shl
+    }
+
+    impl<T, const LANES: usize> Shr for Simd<T, LANES> {
+        fn shr
+    }
+}
diff --git a/library/portable-simd/crates/core_simd/src/ops/unary.rs b/library/portable-simd/crates/core_simd/src/ops/unary.rs
new file mode 100644 (file)
index 0000000..4ebea56
--- /dev/null
@@ -0,0 +1,77 @@
+use crate::simd::intrinsics;
+use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
+use core::ops::{Neg, Not}; // unary ops
+
+macro_rules! neg {
+    ($(impl<const LANES: usize> Neg for Simd<$scalar:ty, LANES>)*) => {
+        $(impl<const LANES: usize> Neg for Simd<$scalar, LANES>
+        where
+            $scalar: SimdElement,
+            LaneCount<LANES>: SupportedLaneCount,
+        {
+            type Output = Self;
+
+            #[inline]
+            #[must_use = "operator returns a new vector without mutating the input"]
+            fn neg(self) -> Self::Output {
+                unsafe { intrinsics::simd_neg(self) }
+            }
+        })*
+    }
+}
+
+neg! {
+    impl<const LANES: usize> Neg for Simd<f32, LANES>
+
+    impl<const LANES: usize> Neg for Simd<f64, LANES>
+
+    impl<const LANES: usize> Neg for Simd<i8, LANES>
+
+    impl<const LANES: usize> Neg for Simd<i16, LANES>
+
+    impl<const LANES: usize> Neg for Simd<i32, LANES>
+
+    impl<const LANES: usize> Neg for Simd<i64, LANES>
+
+    impl<const LANES: usize> Neg for Simd<isize, LANES>
+}
+
+macro_rules! not {
+    ($(impl<const LANES: usize> Not for Simd<$scalar:ty, LANES>)*) => {
+        $(impl<const LANES: usize> Not for Simd<$scalar, LANES>
+        where
+            $scalar: SimdElement,
+            LaneCount<LANES>: SupportedLaneCount,
+        {
+            type Output = Self;
+
+            #[inline]
+            #[must_use = "operator returns a new vector without mutating the input"]
+            fn not(self) -> Self::Output {
+                self ^ (Simd::splat(!(0 as $scalar)))
+            }
+        })*
+    }
+}
+
+not! {
+    impl<const LANES: usize> Not for Simd<i8, LANES>
+
+    impl<const LANES: usize> Not for Simd<i16, LANES>
+
+    impl<const LANES: usize> Not for Simd<i32, LANES>
+
+    impl<const LANES: usize> Not for Simd<i64, LANES>
+
+    impl<const LANES: usize> Not for Simd<isize, LANES>
+
+    impl<const LANES: usize> Not for Simd<u8, LANES>
+
+    impl<const LANES: usize> Not for Simd<u16, LANES>
+
+    impl<const LANES: usize> Not for Simd<u32, LANES>
+
+    impl<const LANES: usize> Not for Simd<u64, LANES>
+
+    impl<const LANES: usize> Not for Simd<usize, LANES>
+}
index db0640aae7905aa640816a03b8308eea86b5a923..e79a185816bfbfcd3d9745003b66e50f0f6ad57b 100644 (file)
@@ -2,7 +2,8 @@
     simd_reduce_add_ordered, simd_reduce_and, simd_reduce_max, simd_reduce_min,
     simd_reduce_mul_ordered, simd_reduce_or, simd_reduce_xor,
 };
-use crate::simd::{LaneCount, Simd, SupportedLaneCount};
+use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
+use core::ops::{BitAnd, BitOr, BitXor};
 
 macro_rules! impl_integer_reductions {
     { $scalar:ty } => {
@@ -22,27 +23,6 @@ pub fn horizontal_product(self) -> $scalar {
                 unsafe { simd_reduce_mul_ordered(self, 1) }
             }
 
-            /// Horizontal bitwise "and".  Returns the cumulative bitwise "and" across the lanes of
-            /// the vector.
-            #[inline]
-            pub fn horizontal_and(self) -> $scalar {
-                unsafe { simd_reduce_and(self) }
-            }
-
-            /// Horizontal bitwise "or".  Returns the cumulative bitwise "or" across the lanes of
-            /// the vector.
-            #[inline]
-            pub fn horizontal_or(self) -> $scalar {
-                unsafe { simd_reduce_or(self) }
-            }
-
-            /// Horizontal bitwise "xor".  Returns the cumulative bitwise "xor" across the lanes of
-            /// the vector.
-            #[inline]
-            pub fn horizontal_xor(self) -> $scalar {
-                unsafe { simd_reduce_xor(self) }
-            }
-
             /// Horizontal maximum.  Returns the maximum lane in the vector.
             #[inline]
             pub fn horizontal_max(self) -> $scalar {
@@ -121,3 +101,45 @@ pub fn horizontal_min(self) -> $scalar {
 
 impl_float_reductions! { f32 }
 impl_float_reductions! { f64 }
+
+impl<T, const LANES: usize> Simd<T, LANES>
+where
+    Self: BitAnd<Self, Output = Self>,
+    T: SimdElement + BitAnd<T, Output = T>,
+    LaneCount<LANES>: SupportedLaneCount,
+{
+    /// Horizontal bitwise "and".  Returns the cumulative bitwise "and" across the lanes of
+    /// the vector.
+    #[inline]
+    pub fn horizontal_and(self) -> T {
+        unsafe { simd_reduce_and(self) }
+    }
+}
+
+impl<T, const LANES: usize> Simd<T, LANES>
+where
+    Self: BitOr<Self, Output = Self>,
+    T: SimdElement + BitOr<T, Output = T>,
+    LaneCount<LANES>: SupportedLaneCount,
+{
+    /// Horizontal bitwise "or".  Returns the cumulative bitwise "or" across the lanes of
+    /// the vector.
+    #[inline]
+    pub fn horizontal_or(self) -> T {
+        unsafe { simd_reduce_or(self) }
+    }
+}
+
+impl<T, const LANES: usize> Simd<T, LANES>
+where
+    Self: BitXor<Self, Output = Self>,
+    T: SimdElement + BitXor<T, Output = T>,
+    LaneCount<LANES>: SupportedLaneCount,
+{
+    /// Horizontal bitwise "xor".  Returns the cumulative bitwise "xor" across the lanes of
+    /// the vector.
+    #[inline]
+    pub fn horizontal_xor(self) -> T {
+        unsafe { simd_reduce_xor(self) }
+    }
+}
index d976231a03a84afd7e87208eb201f2a8a3fdac49..5d696ebf76ec299bccb57673f6a13c869f8ad09f 100644 (file)
@@ -17,6 +17,7 @@ impl<T, const LANES: usize> Sealed<Mask<T::Mask, LANES>> for Simd<T, LANES>
     LaneCount<LANES>: SupportedLaneCount,
 {
     #[inline]
+    #[must_use = "method returns a new vector and does not mutate the original inputs"]
     fn select(mask: Mask<T::Mask, LANES>, true_values: Self, false_values: Self) -> Self {
         unsafe { intrinsics::simd_select(mask.to_int(), true_values, false_values) }
     }
@@ -35,6 +36,7 @@ impl<T, const LANES: usize> Sealed<Self> for Mask<T, LANES>
     LaneCount<LANES>: SupportedLaneCount,
 {
     #[inline]
+    #[must_use = "method returns a new vector and does not mutate the original inputs"]
     fn select(mask: Self, true_values: Self, false_values: Self) -> Self {
         mask & true_values | !mask & false_values
     }
@@ -80,6 +82,7 @@ impl<T, const LANES: usize> Mask<T, LANES>
     /// assert_eq!(c.to_array(), [true, false, true, false]);
     /// ```
     #[inline]
+    #[must_use = "method returns a new vector and does not mutate the original inputs"]
     pub fn select<S: Select<Self>>(self, true_values: S, false_values: S) -> S {
         S::select(self, true_values, false_values)
     }
index 62cda68f0a94921c91f44c1d370e5092c286c3e2..bdc489774a54a43e1293a31a754cfb9da72ecc3d 100644 (file)
@@ -87,6 +87,8 @@ pub trait Swizzle<const INPUT_LANES: usize, const OUTPUT_LANES: usize> {
     /// Create a new vector from the lanes of `vector`.
     ///
     /// Lane `i` of the output is `vector[Self::INDEX[i]]`.
+    #[inline]
+    #[must_use = "method returns a new vector and does not mutate the original inputs"]
     fn swizzle<T>(vector: Simd<T, INPUT_LANES>) -> Simd<T, OUTPUT_LANES>
     where
         T: SimdElement,
@@ -106,6 +108,8 @@ pub trait Swizzle2<const INPUT_LANES: usize, const OUTPUT_LANES: usize> {
     ///
     /// Lane `i` is `first[j]` when `Self::INDEX[i]` is `First(j)`, or `second[j]` when it is
     /// `Second(j)`.
+    #[inline]
+    #[must_use = "method returns a new vector and does not mutate the original inputs"]
     fn swizzle2<T>(
         first: Simd<T, INPUT_LANES>,
         second: Simd<T, INPUT_LANES>,
@@ -182,6 +186,7 @@ impl<T, const LANES: usize> Simd<T, LANES>
 {
     /// Reverse the order of the lanes in the vector.
     #[inline]
+    #[must_use = "method returns a new vector and does not mutate the original inputs"]
     pub fn reverse(self) -> Self {
         const fn reverse_index<const LANES: usize>() -> [usize; LANES] {
             let mut index = [0; LANES];
@@ -206,6 +211,7 @@ impl<const LANES: usize> Swizzle<LANES, LANES> for Reverse {
     /// while the last `LANES - OFFSET` elements move to the front. After calling `rotate_lanes_left`,
     /// the element previously in lane `OFFSET` will become the first element in the slice.
     #[inline]
+    #[must_use = "method returns a new vector and does not mutate the original inputs"]
     pub fn rotate_lanes_left<const OFFSET: usize>(self) -> Self {
         const fn rotate_index<const OFFSET: usize, const LANES: usize>() -> [usize; LANES] {
             let offset = OFFSET % LANES;
@@ -231,6 +237,7 @@ impl<const OFFSET: usize, const LANES: usize> Swizzle<LANES, LANES> for Rotate<O
     /// the end while the last `OFFSET` elements move to the front. After calling `rotate_lanes_right`,
     /// the element previously at index `LANES - OFFSET` will become the first element in the slice.
     #[inline]
+    #[must_use = "method returns a new vector and does not mutate the original inputs"]
     pub fn rotate_lanes_right<const OFFSET: usize>(self) -> Self {
         const fn rotate_index<const OFFSET: usize, const LANES: usize>() -> [usize; LANES] {
             let offset = LANES - OFFSET % LANES;
@@ -273,6 +280,7 @@ impl<const OFFSET: usize, const LANES: usize> Swizzle<LANES, LANES> for Rotate<O
     /// assert_eq!(y.to_array(), [2, 6, 3, 7]);
     /// ```
     #[inline]
+    #[must_use = "method returns a new vector and does not mutate the original inputs"]
     pub fn interleave(self, other: Self) -> (Self, Self) {
         const fn lo<const LANES: usize>() -> [Which; LANES] {
             let mut idx = [Which::First(0); LANES];
@@ -336,6 +344,7 @@ impl<const LANES: usize> Swizzle2<LANES, LANES> for Hi {
     /// assert_eq!(y.to_array(), [4, 5, 6, 7]);
     /// ```
     #[inline]
+    #[must_use = "method returns a new vector and does not mutate the original inputs"]
     pub fn deinterleave(self, other: Self) -> (Self, Self) {
         const fn even<const LANES: usize>() -> [Which; LANES] {
             let mut idx = [Which::First(0); LANES];
index c09d0ac84d246c24d8051a02a3373c93d75930b7..4a4b23238c4ab3770d5357ffa962ae36d9afb999 100644 (file)
@@ -15,6 +15,7 @@ impl<const LANES: usize> Simd<$type, LANES>
             /// Raw transmutation to an unsigned integer vector type with the
             /// same size and number of lanes.
             #[inline]
+            #[must_use = "method returns a new vector and does not mutate the original value"]
             pub fn to_bits(self) -> Simd<$bits_ty, LANES> {
                 assert_eq!(core::mem::size_of::<Self>(), core::mem::size_of::<Simd<$bits_ty, LANES>>());
                 unsafe { core::mem::transmute_copy(&self) }
@@ -23,6 +24,7 @@ pub fn to_bits(self) -> Simd<$bits_ty, LANES> {
             /// Raw transmutation from an unsigned integer vector type with the
             /// same size and number of lanes.
             #[inline]
+            #[must_use = "method returns a new vector and does not mutate the original value"]
             pub fn from_bits(bits: Simd<$bits_ty, LANES>) -> Self {
                 assert_eq!(core::mem::size_of::<Self>(), core::mem::size_of::<Simd<$bits_ty, LANES>>());
                 unsafe { core::mem::transmute_copy(&bits) }
@@ -31,6 +33,7 @@ pub fn from_bits(bits: Simd<$bits_ty, LANES>) -> Self {
             /// Produces a vector where every lane has the absolute value of the
             /// equivalently-indexed lane in `self`.
             #[inline]
+            #[must_use = "method returns a new vector and does not mutate the original value"]
             pub fn abs(self) -> Self {
                 unsafe { intrinsics::simd_fabs(self) }
             }
@@ -44,6 +47,7 @@ pub fn abs(self) -> Self {
             /// hardware in mind.
             #[cfg(feature = "std")]
             #[inline]
+            #[must_use = "method returns a new vector and does not mutate the original value"]
             pub fn mul_add(self, a: Self, b: Self) -> Self {
                 unsafe { intrinsics::simd_fma(self, a, b) }
             }
@@ -51,6 +55,7 @@ pub fn mul_add(self, a: Self, b: Self) -> Self {
             /// Produces a vector where every lane has the square root value
             /// of the equivalently-indexed lane in `self`
             #[inline]
+            #[must_use = "method returns a new vector and does not mutate the original value"]
             #[cfg(feature = "std")]
             pub fn sqrt(self) -> Self {
                 unsafe { intrinsics::simd_fsqrt(self) }
@@ -58,12 +63,14 @@ pub fn sqrt(self) -> Self {
 
             /// Takes the reciprocal (inverse) of each lane, `1/x`.
             #[inline]
+            #[must_use = "method returns a new vector and does not mutate the original value"]
             pub fn recip(self) -> Self {
                 Self::splat(1.0) / self
             }
 
             /// Converts each lane from radians to degrees.
             #[inline]
+            #[must_use = "method returns a new vector and does not mutate the original value"]
             pub fn to_degrees(self) -> Self {
                 // to_degrees uses a special constant for better precision, so extract that constant
                 self * Self::splat(<$type>::to_degrees(1.))
@@ -71,6 +78,7 @@ pub fn to_degrees(self) -> Self {
 
             /// Converts each lane from degrees to radians.
             #[inline]
+            #[must_use = "method returns a new vector and does not mutate the original value"]
             pub fn to_radians(self) -> Self {
                 self * Self::splat(<$type>::to_radians(1.))
             }
@@ -78,6 +86,7 @@ pub fn to_radians(self) -> Self {
             /// Returns true for each lane if it has a positive sign, including
             /// `+0.0`, `NaN`s with positive sign bit and positive infinity.
             #[inline]
+            #[must_use = "method returns a new mask and does not mutate the original value"]
             pub fn is_sign_positive(self) -> Mask<$mask_ty, LANES> {
                 !self.is_sign_negative()
             }
@@ -85,6 +94,7 @@ pub fn is_sign_positive(self) -> Mask<$mask_ty, LANES> {
             /// Returns true for each lane if it has a negative sign, including
             /// `-0.0`, `NaN`s with negative sign bit and negative infinity.
             #[inline]
+            #[must_use = "method returns a new mask and does not mutate the original value"]
             pub fn is_sign_negative(self) -> Mask<$mask_ty, LANES> {
                 let sign_bits = self.to_bits() & Simd::splat((!0 >> 1) + 1);
                 sign_bits.lanes_gt(Simd::splat(0))
@@ -92,24 +102,28 @@ pub fn is_sign_negative(self) -> Mask<$mask_ty, LANES> {
 
             /// Returns true for each lane if its value is `NaN`.
             #[inline]
+            #[must_use = "method returns a new mask and does not mutate the original value"]
             pub fn is_nan(self) -> Mask<$mask_ty, LANES> {
                 self.lanes_ne(self)
             }
 
             /// Returns true for each lane if its value is positive infinity or negative infinity.
             #[inline]
+            #[must_use = "method returns a new mask and does not mutate the original value"]
             pub fn is_infinite(self) -> Mask<$mask_ty, LANES> {
                 self.abs().lanes_eq(Self::splat(<$type>::INFINITY))
             }
 
             /// Returns true for each lane if its value is neither infinite nor `NaN`.
             #[inline]
+            #[must_use = "method returns a new mask and does not mutate the original value"]
             pub fn is_finite(self) -> Mask<$mask_ty, LANES> {
                 self.abs().lanes_lt(Self::splat(<$type>::INFINITY))
             }
 
             /// Returns true for each lane if its value is subnormal.
             #[inline]
+            #[must_use = "method returns a new mask and does not mutate the original value"]
             pub fn is_subnormal(self) -> Mask<$mask_ty, LANES> {
                 self.abs().lanes_ne(Self::splat(0.0)) & (self.to_bits() & Self::splat(<$type>::INFINITY).to_bits()).lanes_eq(Simd::splat(0))
             }
@@ -117,6 +131,7 @@ pub fn is_subnormal(self) -> Mask<$mask_ty, LANES> {
             /// Returns true for each lane if its value is neither neither zero, infinite,
             /// subnormal, or `NaN`.
             #[inline]
+            #[must_use = "method returns a new mask and does not mutate the original value"]
             pub fn is_normal(self) -> Mask<$mask_ty, LANES> {
                 !(self.abs().lanes_eq(Self::splat(0.0)) | self.is_nan() | self.is_subnormal() | self.is_infinite())
             }
@@ -127,6 +142,7 @@ pub fn is_normal(self) -> Mask<$mask_ty, LANES> {
             /// * `-1.0` if the number is negative, `-0.0`, or `NEG_INFINITY`
             /// * `NAN` if the number is `NAN`
             #[inline]
+            #[must_use = "method returns a new vector and does not mutate the original value"]
             pub fn signum(self) -> Self {
                 self.is_nan().select(Self::splat(<$type>::NAN), Self::splat(1.0).copysign(self))
             }
@@ -135,6 +151,7 @@ pub fn signum(self) -> Self {
             ///
             /// If any lane is a `NAN`, then a `NAN` with the sign of `sign` is returned.
             #[inline]
+            #[must_use = "method returns a new vector and does not mutate the original value"]
             pub fn copysign(self, sign: Self) -> Self {
                 let sign_bit = sign.to_bits() & Self::splat(-0.).to_bits();
                 let magnitude = self.to_bits() & !Self::splat(-0.).to_bits();
@@ -145,6 +162,7 @@ pub fn copysign(self, sign: Self) -> Self {
             ///
             /// If one of the values is `NAN`, then the other value is returned.
             #[inline]
+            #[must_use = "method returns a new vector and does not mutate the original value"]
             pub fn min(self, other: Self) -> Self {
                 // TODO consider using an intrinsic
                 self.is_nan().select(
@@ -157,6 +175,7 @@ pub fn min(self, other: Self) -> Self {
             ///
             /// If one of the values is `NAN`, then the other value is returned.
             #[inline]
+            #[must_use = "method returns a new vector and does not mutate the original value"]
             pub fn max(self, other: Self) -> Self {
                 // TODO consider using an intrinsic
                 self.is_nan().select(
@@ -171,6 +190,7 @@ pub fn max(self, other: Self) -> Self {
             /// greater than `max`, and the corresponding lane in `min` if the lane is less
             /// than `min`.  Otherwise returns the lane in `self`.
             #[inline]
+            #[must_use = "method returns a new vector and does not mutate the original value"]
             pub fn clamp(self, min: Self, max: Self) -> Self {
                 assert!(
                     min.lanes_le(max).all(),
index ac9b98ca031a64729718d71cf58ea56603146cac..c668d9a6eaee35785e7eb84b7bc5a01f7d317bc8 100644 (file)
@@ -23,7 +23,7 @@ pub fn splat(ptr: *const T) -> Self {
     pub fn wrapping_add(self, addend: Simd<usize, LANES>) -> Self {
         unsafe {
             let x: Simd<usize, LANES> = mem::transmute_copy(&self);
-            mem::transmute_copy(&{ x + (addend * mem::size_of::<T>()) })
+            mem::transmute_copy(&{ x + (addend * Simd::splat(mem::size_of::<T>())) })
         }
     }
 }
@@ -49,7 +49,7 @@ pub fn splat(ptr: *mut T) -> Self {
     pub fn wrapping_add(self, addend: Simd<usize, LANES>) -> Self {
         unsafe {
             let x: Simd<usize, LANES> = mem::transmute_copy(&self);
-            mem::transmute_copy(&{ x + (addend * mem::size_of::<T>()) })
+            mem::transmute_copy(&{ x + (addend * Simd::splat(mem::size_of::<T>())) })
         }
     }
 }
index d3c19ccc539ad4564bab7e35e2de57881686dcf4..0dd47015ed22f21b63331b1c273e9d1697784fd1 100644 (file)
@@ -8,10 +8,10 @@
 
 from_transmute! { unsafe u8x16 => __m128i }
 from_transmute! { unsafe u8x32 => __m256i }
-//from_transmute! { unsafe u8x64 => __m512i }
+from_transmute! { unsafe u8x64 => __m512i }
 from_transmute! { unsafe i8x16 => __m128i }
 from_transmute! { unsafe i8x32 => __m256i }
-//from_transmute! { unsafe i8x64 => __m512i }
+from_transmute! { unsafe i8x64 => __m512i }
 
 from_transmute! { unsafe u16x8 => __m128i }
 from_transmute! { unsafe u16x16 => __m256i }
diff --git a/library/portable-simd/crates/core_simd/tests/autoderef.rs b/library/portable-simd/crates/core_simd/tests/autoderef.rs
new file mode 100644 (file)
index 0000000..9359da1
--- /dev/null
@@ -0,0 +1,22 @@
+// Test that we handle all our "auto-deref" cases correctly.
+#![feature(portable_simd)]
+use core_simd::f32x4;
+
+#[cfg(target_arch = "wasm32")]
+use wasm_bindgen_test::*;
+
+#[cfg(target_arch = "wasm32")]
+wasm_bindgen_test_configure!(run_in_browser);
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+fn deref() {
+    let x = f32x4::splat(1.0);
+    let y = f32x4::splat(2.0);
+    let a = &x;
+    let b = &y;
+    assert_eq!(f32x4::splat(3.0), x + y);
+    assert_eq!(f32x4::splat(3.0), x + b);
+    assert_eq!(f32x4::splat(3.0), a + y);
+    assert_eq!(f32x4::splat(3.0), a + b);
+}
index 31b7ee2069590a516970b5925c0628346dd64ca2..43ddde4c55e0162db189c5c4270a31695f69f021 100644 (file)
@@ -38,22 +38,6 @@ fn normal<const LANES: usize>() {
                     );
                 }
 
-                fn scalar_rhs<const LANES: usize>() {
-                    test_helpers::test_binary_scalar_rhs_elementwise(
-                        &<Simd<$scalar, LANES> as core::ops::$trait<$scalar>>::$fn,
-                        &$scalar_fn,
-                        &|_, _| true,
-                    );
-                }
-
-                fn scalar_lhs<const LANES: usize>() {
-                    test_helpers::test_binary_scalar_lhs_elementwise(
-                        &<$scalar as core::ops::$trait<Simd<$scalar, LANES>>>::$fn,
-                        &$scalar_fn,
-                        &|_, _| true,
-                    );
-                }
-
                 fn assign<const LANES: usize>() {
                     test_helpers::test_binary_elementwise(
                         &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
@@ -61,14 +45,6 @@ fn assign<const LANES: usize>() {
                         &|_, _| true,
                     );
                 }
-
-                fn assign_scalar_rhs<const LANES: usize>() {
-                    test_helpers::test_binary_scalar_rhs_elementwise(
-                        &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
-                        &$scalar_fn,
-                        &|_, _| true,
-                    );
-                }
             }
         }
     };
@@ -99,22 +75,6 @@ fn normal<const LANES: usize>() {
                     );
                 }
 
-                fn scalar_rhs<const LANES: usize>() {
-                    test_helpers::test_binary_scalar_rhs_elementwise(
-                        &<Simd<$scalar, LANES> as core::ops::$trait<$scalar>>::$fn,
-                        &$scalar_fn,
-                        &|x, y| x.iter().all(|x| $check_fn(*x, y)),
-                    );
-                }
-
-                fn scalar_lhs<const LANES: usize>() {
-                    test_helpers::test_binary_scalar_lhs_elementwise(
-                        &<$scalar as core::ops::$trait<Simd<$scalar, LANES>>>::$fn,
-                        &$scalar_fn,
-                        &|x, y| y.iter().all(|y| $check_fn(x, *y)),
-                    );
-                }
-
                 fn assign<const LANES: usize>() {
                     test_helpers::test_binary_elementwise(
                         &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
@@ -122,14 +82,6 @@ fn assign<const LANES: usize>() {
                         &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
                     )
                 }
-
-                fn assign_scalar_rhs<const LANES: usize>() {
-                    test_helpers::test_binary_scalar_rhs_elementwise(
-                        &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
-                        &$scalar_fn,
-                        &|x, y| x.iter().all(|x| $check_fn(*x, y)),
-                    )
-                }
             }
         }
     };
index 5c6478876f30be88797794d6c571fceb62193a1a..7edd609638171da96b1d7b9c38d3740a6f3724e8 100644 (file)
@@ -376,6 +376,12 @@ fn lanes_16() {
                 fn lanes_32() {
                     implementation::<32>();
                 }
+
+                #[test]
+                #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+                fn lanes_64() {
+                    implementation::<64>();
+                }
             }
         )*
     }
@@ -431,6 +437,12 @@ fn lanes_16() {
                 fn lanes_32() {
                     implementation::<32>();
                 }
+
+                #[test]
+                #[should_panic]
+                fn lanes_64() {
+                    implementation::<64>();
+                }
             }
         )*
     }
index 7f3bb836754ed5396607dc6a4889a6f249edebc0..019b64c395eda5dbe38288ed8782b025125208f5 100644 (file)
@@ -81,9 +81,9 @@
 //! [`OsStr`] and Rust strings work similarly to those for [`CString`]
 //! and [`CStr`].
 //!
-//! * [`OsString`] represents an owned string in whatever
-//! representation the operating system prefers. In the Rust standard
-//! library, various APIs that transfer strings to/from the operating
+//! * [`OsString`] losslessly represents an owned platform string. However, this
+//! representation is not necessarily in a form native to the platform.
+//! In the Rust standard library, various APIs that transfer strings to/from the operating
 //! system use [`OsString`] instead of plain strings. For example,
 //! [`env::var_os()`] is used to query environment variables; it
 //! returns an <code>[Option]<[OsString]></code>. If the environment variable
@@ -92,9 +92,9 @@
 //! your code can detect errors in case the environment variable did
 //! not in fact contain valid Unicode data.
 //!
-//! * [`OsStr`] represents a borrowed reference to a string in a
-//! format that can be passed to the operating system. It can be
-//! converted into a UTF-8 Rust string slice in a similar way to
+//! * [`OsStr`] losslessly represents a borrowed reference to a platform string.
+//! However, this representation is not necessarily in a form native to the platform.
+//! It can be converted into a UTF-8 Rust string slice in a similar way to
 //! [`OsString`].
 //!
 //! # Conversions
 //!
 //! ## On Windows
 //!
+//! An [`OsStr`] can be losslessly converted to a native Windows string. And
+//! a native Windows string can be losslessly converted to an [`OsString`].
+//!
 //! On Windows, [`OsStr`] implements the
 //! <code>std::os::windows::ffi::[OsStrExt][windows.OsStrExt]</code> trait,
 //! which provides an [`encode_wide`] method. This provides an
-//! iterator that can be [`collect`]ed into a vector of [`u16`].
+//! iterator that can be [`collect`]ed into a vector of [`u16`]. After a nul
+//! characters is appended, this is the same as a native Windows string.
 //!
 //! Additionally, on Windows [`OsString`] implements the
 //! <code>std::os::windows:ffi::[OsStringExt][windows.OsStringExt]</code>
-//! trait, which provides a [`from_wide`] method. The result of this
-//! method is an [`OsString`] which can be round-tripped to a Windows
-//! string losslessly.
+//! trait, which provides a [`from_wide`] method to convert a native Windows
+//! string (without the terminating nul character) to an [`OsString`].
 //!
 //! [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value
 //! [Unicode code point]: https://www.unicode.org/glossary/#code_point
index 01392ffab79cac81f03288b789efce77ce064de4..f0b38d2984544e4fc99f176e131d6d90f7820775 100644 (file)
@@ -69,7 +69,8 @@ macro_rules! type_alias {
             target_arch = "aarch64",
             target_arch = "arm",
             target_arch = "powerpc",
-            target_arch = "powerpc64"
+            target_arch = "powerpc64",
+            target_arch = "riscv64"
         )
     ),
     all(
@@ -112,7 +113,8 @@ macro_rules! type_alias {
             target_arch = "aarch64",
             target_arch = "arm",
             target_arch = "powerpc",
-            target_arch = "powerpc64"
+            target_arch = "powerpc64",
+            target_arch = "riscv64"
         )
     ),
     all(
index 22f2e405a1e88d29356539b74453b4e71b16c793..ca7ad53203045e801d711e072a75a240c823c599 100644 (file)
@@ -265,7 +265,7 @@ def default_build_triple(verbose):
         err = "unknown OS type: {}".format(ostype)
         sys.exit(err)
 
-    if cputype == 'powerpc' and ostype == 'unknown-freebsd':
+    if cputype in ['powerpc', 'riscv'] and ostype == 'unknown-freebsd':
         cputype = subprocess.check_output(
               ['uname', '-p']).strip().decode(default_encoding)
     cputype_mapper = {
index 19e5fffcc2ddb1d3b267862a465961996b09ab46..ef635ee26df57bd40cbda17deb9be334587c7f98 100644 (file)
@@ -1,4 +1,4 @@
 Change this file to make users of the `download-ci-llvm` configuration download
 a new version of LLVM from CI, even if the LLVM submodule hasn’t changed.
 
-Last change is for: https://github.com/rust-lang/rust/pull/88069
+Last change is for: https://github.com/rust-lang/rust/pull/91229
index 37578e30f6d0f698a4185ac5d23e0c410c5a063c..4a754e6da12097293417c5f401728bf0c7e07ae2 100644 (file)
@@ -249,9 +249,14 @@ fn run(self, builder: &Builder<'_>) -> PathBuf {
             }
         }
 
-        if target.starts_with("riscv") {
-            // In RISC-V, using C++ atomics require linking to `libatomic` but the LLVM build
-            // system check cannot detect this. Therefore it is set manually here.
+        if !target.contains("freebsd") && target.starts_with("riscv") {
+            // RISC-V GCC erroneously requires linking against
+            // `libatomic` when using 1-byte and 2-byte C++
+            // atomics but the LLVM build system check cannot
+            // detect this. Therefore it is set manually here.
+            // FreeBSD uses Clang as its system compiler and
+            // provides no libatomic in its base system so does
+            // not want this.
             if !builder.config.llvm_tools_enabled {
                 cfg.define("CMAKE_EXE_LINKER_FLAGS", "-latomic");
             } else {
index e6b6b6e53b99d32126279b32546d09a7fc798c10..ab588ccc24999cb80bcd52c1a4da4db203770a4b 100644 (file)
@@ -1,5 +1,18 @@
+FROM ubuntu:20.04
+RUN apt-get update && \
+    apt-get install -y --no-install-recommends \
+        curl \
+        ca-certificates
+WORKDIR /tmp
+RUN curl -f https://curl.se/ca/cacert.pem -o cacert.pem
+
 FROM ubuntu:16.04
 
+# The ca-certificates in ubuntu-16 is too old, so update the certificates
+# with something more recent.
+COPY --from=0 /tmp/cacert.pem /tmp/cacert.pem
+ENV CURL_CA_BUNDLE /tmp/cacert.pem
+
 COPY scripts/cross-apt-packages.sh /scripts/
 RUN sh /scripts/cross-apt-packages.sh
 
index 61cc000dca55529ea82e1c7ded8fd7d0bedf2072..ee4fd759b469b60ddb82b9ae29eedacf3933117d 100644 (file)
@@ -1,5 +1,18 @@
+FROM ubuntu:20.04
+RUN apt-get update && \
+    apt-get install -y --no-install-recommends \
+        curl \
+        ca-certificates
+WORKDIR /tmp
+RUN curl -f https://curl.se/ca/cacert.pem -o cacert.pem
+
 FROM ubuntu:16.04
 
+# The ca-certificates in ubuntu-16 is too old, so update the certificates
+# with something more recent.
+COPY --from=0 /tmp/cacert.pem /tmp/cacert.pem
+ENV CURL_CA_BUNDLE /tmp/cacert.pem
+
 COPY scripts/cross-apt-packages.sh /scripts/
 RUN sh /scripts/cross-apt-packages.sh
 
index 66eb4137a87ca1a3cbae0ae01d088f33ee668faa..b11a1d3feb2eefa5f44efd2a915f26d553b71f4c 100644 (file)
@@ -1,5 +1,18 @@
+FROM ubuntu:20.04
+RUN apt-get update && \
+    apt-get install -y --no-install-recommends \
+        curl \
+        ca-certificates
+WORKDIR /tmp
+RUN curl -f https://curl.se/ca/cacert.pem -o cacert.pem
+
 FROM ubuntu:16.04
 
+# The ca-certificates in ubuntu-16 is too old, so update the certificates
+# with something more recent.
+COPY --from=0 /tmp/cacert.pem /tmp/cacert.pem
+ENV CURL_CA_BUNDLE /tmp/cacert.pem
+
 COPY scripts/cross-apt-packages.sh /scripts/
 RUN sh /scripts/cross-apt-packages.sh
 
index c13f63911f8fae06089859ba6ac6dce173ce665c..55ca23b293d5e2336b801b15991e71097f30d84c 100644 (file)
@@ -1,5 +1,18 @@
+FROM ubuntu:20.04
+RUN apt-get update && \
+    apt-get install -y --no-install-recommends \
+        curl \
+        ca-certificates
+WORKDIR /tmp
+RUN curl -f https://curl.se/ca/cacert.pem -o cacert.pem
+
 FROM ubuntu:16.04
 
+# The ca-certificates in ubuntu-16 is too old, so update the certificates
+# with something more recent.
+COPY --from=0 /tmp/cacert.pem /tmp/cacert.pem
+ENV CURL_CA_BUNDLE /tmp/cacert.pem
+
 COPY scripts/cross-apt-packages.sh /scripts/
 RUN sh /scripts/cross-apt-packages.sh
 
index a5e0c5b2c5f9054be3b961aea2c7edfeea591de8..5f9358faeb1f46e19b8a23a21e79fd7fe150491e 160000 (submodule)
@@ -1 +1 @@
-Subproject commit a5e0c5b2c5f9054be3b961aea2c7edfeea591de8
+Subproject commit 5f9358faeb1f46e19b8a23a21e79fd7fe150491e
index 8e0ec8c77d8b28b86159fdee9d33a758225ecf9c..beea0a3cdc3885375342fd010f9ad658e6a5e09a 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 8e0ec8c77d8b28b86159fdee9d33a758225ecf9c
+Subproject commit beea0a3cdc3885375342fd010f9ad658e6a5e09a
index c6b4bf831e9a40aec34f53067d20634839a6778b..49681ea4a9fa81173dbe9ffed74b4d4a35eae9e3 160000 (submodule)
@@ -1 +1 @@
-Subproject commit c6b4bf831e9a40aec34f53067d20634839a6778b
+Subproject commit 49681ea4a9fa81173dbe9ffed74b4d4a35eae9e3
index c0f222da23568477155991d391c9ce918e381351..954f3d441ad880737a13e241108f791a4d2a38cd 160000 (submodule)
@@ -1 +1 @@
-Subproject commit c0f222da23568477155991d391c9ce918e381351
+Subproject commit 954f3d441ad880737a13e241108f791a4d2a38cd
index 43f82530210b83cf888282b207ed13d5893da9b2..1ca6a7bd1d73edc4a3e6c7d6a40f5d4b66c1e517 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 43f82530210b83cf888282b207ed13d5893da9b2
+Subproject commit 1ca6a7bd1d73edc4a3e6c7d6a40f5d4b66c1e517
index a2fc9635029c04e692474965a6606f8e286d539a..a374e7d8bb6b79de45b92295d06b4ac0ef35bc09 160000 (submodule)
@@ -1 +1 @@
-Subproject commit a2fc9635029c04e692474965a6606f8e286d539a
+Subproject commit a374e7d8bb6b79de45b92295d06b4ac0ef35bc09
index 4da3491c5863475c969c59902e9e26197594426f..f4f659ffa273ee57b8f8dfecc2152d81ffe861ff 100644 (file)
@@ -267,6 +267,7 @@ target | std | host | notes
 `riscv32gc-unknown-linux-gnu` |   |   | RISC-V Linux (kernel 5.4, glibc 2.33)
 `riscv32gc-unknown-linux-musl` |   |   | RISC-V Linux (kernel 5.4, musl + RISCV32 support patches)
 `riscv32imc-esp-espidf` | ✓ |  | RISC-V ESP-IDF
+`riscv64gc-unknown-freebsd` |   |   | RISC-V FreeBSD
 `riscv64gc-unknown-linux-musl` |   |   | RISC-V Linux (kernel 4.20, musl 1.2.0)
 `s390x-unknown-linux-musl` |  |  | S390x Linux (kernel 2.6.32, MUSL)
 `sparc-unknown-linux-gnu` | ✓ |  | 32-bit SPARC Linux
index 6be53fb4cfed4c53fd5eca1f7fdf74d5f4eb9593..aea55d4f4b69cb0a19a8c0f589e584def8cbbfd2 100644 (file)
@@ -359,9 +359,8 @@ are added.
 # fn foo() {}
 ```
 
-`edition2018` tells `rustdoc` that the code sample should be compiled using
-the 2018 edition of Rust. Similarly, you can specify `edition2015` to compile
-the code with the 2015 edition.
+`edition2015`, `edition2018` and `edition2021` tell `rustdoc`
+that the code sample should be compiled using the respective edition of Rust.
 
 ```rust
 /// Only runs on the 2018 edition.
index fa238a8b3bc6591a55ed18b9403675440464b024..dffa3a8b80f0f38430f31f5e9e1fa50882181e5a 100644 (file)
@@ -32,6 +32,7 @@ Inline assembly is currently supported on the following architectures:
 - wasm32
 - BPF
 - SPIR-V
+- AVR
 
 ## Basic usage
 
@@ -471,6 +472,7 @@ Inline assembly is currently supported on the following architectures:
 - wasm32
 - BPF
 - SPIR-V
+- AVR
 
 Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target.
 
@@ -593,6 +595,11 @@ Here is the list of currently supported register classes:
 | wasm32 | `local` | None\* | `r` |
 | BPF | `reg` | `r[0-10]` | `r` |
 | BPF | `wreg` | `w[0-10]` | `w` |
+| AVR | `reg` | `r[2-25]`, `XH`, `XL`, `ZH`, `ZL` | `r` |
+| AVR | `reg_upper` | `r[16-25]`, `XH`, `XL`, `ZH`, `ZL` | `d` |
+| AVR | `reg_pair` | `r3r2` .. `r25r24`, `X`, `Z` | `r` |
+| AVR | `reg_iw` | `r25r24`, `X`, `Z` | `w` |
+| AVR | `reg_ptr` | `X`, `Z` | `e` |
 
 > **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register.
 >
@@ -648,6 +655,8 @@ Each register class has constraints on which value types they can be used with.
 | wasm32 | `local` | None | `i8` `i16` `i32` `i64` `f32` `f64` |
 | BPF | `reg` | None | `i8` `i16` `i32` `i64` |
 | BPF | `wreg` | `alu32` | `i8` `i16` `i32` |
+| AVR | `reg`, `reg_upper` | None | `i8` |
+| AVR | `reg_pair`, `reg_iw`, `reg_ptr` | None | `i16` |
 
 > **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target).
 
@@ -708,13 +717,17 @@ Some registers have multiple names. These are all treated by the compiler as ide
 | Hexagon | `r30` | `fr` |
 | Hexagon | `r31` | `lr` |
 | BPF | `r[0-10]` | `w[0-10]` |
+| AVR | `XH` | `r27` |
+| AVR | `XL` | `r26` |
+| AVR | `ZH` | `r31` |
+| AVR | `ZL` | `r30` |
 
 Some registers cannot be used for input or output operands:
 
 | Architecture | Unsupported register | Reason |
 | ------------ | -------------------- | ------ |
 | All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. |
-| All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon), `$fp` (MIPS) | The frame pointer cannot be used as an input or output. |
+| All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon), `$fp` (MIPS), `Y` (AVR) | The frame pointer cannot be used as an input or output. |
 | ARM | `r7` or `r11` | On ARM the frame pointer can be either `r7` or `r11` depending on the target. The frame pointer cannot be used as an input or output. |
 | All | `si` (x86-32), `bx` (x86-64), `r6` (ARM), `x19` (AArch64), `r19` (Hexagon), `x9` (RISC-V) | This is used internally by LLVM as a "base pointer" for functions with complex stack frames. |
 | x86 | `k0` | This is a constant zero register which can't be modified. |
@@ -732,11 +745,13 @@ Some registers cannot be used for input or output operands:
 | RISC-V | `x0` | This is a constant zero register which can't be modified. |
 | RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. |
 | Hexagon | `lr` | This is the link register which cannot be used as an input or output. |
+| AVR | `r0`, `r1`, `r1r0` | Due to an issue in LLVM, the `r0` and `r1` registers cannot be used as inputs or outputs.  If modified, they must be restored to their original values before the end of the block. |
 
 In some cases LLVM will allocate a "reserved register" for `reg` operands even though this register cannot be explicitly specified. Assembly code making use of reserved registers should be careful since `reg` operands may alias with those registers. Reserved registers are the frame pointer and base pointer
 - The frame pointer and LLVM base pointer on all architectures.
 - `r9` on ARM.
 - `x18` on AArch64.
+- `r0` and `r1` on AVR.
 
 ## Template modifiers
 
@@ -882,6 +897,8 @@ The compiler performs some additional checks on options:
   - RISC-V
     - Floating-point exception flags in `fcsr` (`fflags`).
     - Vector extension state (`vtype`, `vl`, `vcsr`).
+  - AVR
+    - The status register `SREG`.
 - On x86, the direction flag (DF in `EFLAGS`) is clear on entry to an asm block and must be clear on exit.
   - Behavior is undefined if the direction flag is set on exiting an asm block.
 - The requirement of restoring the stack pointer and non-output registers to their original value only applies when exiting an `asm!` block.
index d811c85ea58d1df2ac9ba658241d0431d1f9c3b5..545b409175ee6e03d7397be71cd80b0d28fb2ff0 100644 (file)
@@ -1447,7 +1447,7 @@ fn init_id_map() -> FxHashMap<String, usize> {
     map.insert("theme-choices".to_owned(), 1);
     map.insert("settings-menu".to_owned(), 1);
     map.insert("help-button".to_owned(), 1);
-    map.insert("main".to_owned(), 1);
+    map.insert("main-content".to_owned(), 1);
     map.insert("search".to_owned(), 1);
     map.insert("crate-search".to_owned(), 1);
     map.insert("render-detail".to_owned(), 1);
index 68ab002f138676d878902e4e4c14d7d434f49f09..d4af3663b624af5b7cded2b37ffe2ab5242914ac 100644 (file)
@@ -12,7 +12,7 @@ fn test_unique_id() {
         "examples",
         "method.into_iter",
         "foo",
-        "main",
+        "main-content",
         "search",
         "methods",
         "examples",
@@ -28,7 +28,7 @@ fn test_unique_id() {
         "examples-2",
         "method.into_iter-1",
         "foo-1",
-        "main-1",
+        "main-content-1",
         "search-1",
         "methods",
         "examples-3",
@@ -219,8 +219,8 @@ fn t(map: &mut IdMap, input: &str, expect: &str) {
     );
     t(
         &mut map,
-        "# Main",
-        "<h2 id=\"main-1\" class=\"section-header\"><a href=\"#main-1\">Main</a></h2>",
+        "# Search",
+        "<h2 id=\"search-1\" class=\"section-header\"><a href=\"#search-1\">Search</a></h2>",
     );
     t(
         &mut map,
index 37ea7b000339f0de6d57ed9b62afc5298eaa6f5d..e35358c56499398581bb215cb3542f0244f7a033 100644 (file)
@@ -4,7 +4,7 @@ of content is hidden by default (depending on the settings too), we have to over
 rules.
 */
 
-#main .attributes {
+#main-content .attributes {
        /* Since there is no toggle (the "[-]") when JS is disabled, no need for this margin either. */
        margin-left: 0 !important;
 }
index fceb508bc4ff5e41461d6398fc073ecb126fd93a..79c7626ffc4bd38319a9c45c68cd6ee91fe1c909 100644 (file)
@@ -111,7 +111,6 @@ body {
        font: 16px/1.4 "Source Serif 4", NanumBarunGothic, serif;
        margin: 0;
        position: relative;
-       padding: 10px 15px 20px 15px;
 
        -webkit-font-feature-settings: "kern", "liga";
        -moz-font-feature-settings: "kern", "liga";
@@ -202,7 +201,7 @@ details.rustdoc-toggle > summary::before,
 div.impl-items > div:not(.docblock):not(.item-info),
 .content ul.crate a.crate, a.srclink,
 /* This selector is for the items listed in the "all items" page. */
-#main > ul.docblock > li > a {
+#main-content > ul.docblock > li > a {
        font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif;
 }
 
@@ -248,6 +247,32 @@ textarea {
 
 /* end tweaks for normalize.css 8 */
 
+.rustdoc {
+       display: flex;
+       flex-direction: row;
+       flex-wrap: nowrap;
+}
+
+main {
+       position: relative;
+       flex-grow: 1;
+       padding: 10px 15px 40px 45px;
+       min-width: 0;
+}
+
+.source main {
+       padding: 15px;
+}
+
+.width-limiter {
+       max-width: 960px;
+       margin-right: auto;
+}
+
+.source .width-limiter {
+       max-width: unset;
+}
+
 details:not(.rustdoc-toggle) summary {
        margin-bottom: .6em;
 }
@@ -285,28 +310,75 @@ li {
 }
 
 .source .content {
-       margin-top: 50px;
        max-width: none;
        overflow: visible;
        margin-left: 0px;
 }
 
 nav.sub {
+       position: relative;
        font-size: 16px;
        text-transform: uppercase;
 }
 
+.sub-container {
+       display: flex;
+       flex-direction: row;
+       flex-wrap: nowrap;
+}
+
+.sub-logo-container {
+       display: none;
+       margin-right: 20px;
+}
+
+.source .sub-logo-container {
+       display: block;
+}
+
+.source .sub-logo-container > img {
+       height: 60px;
+       width: 60px;
+       object-fit: contain;
+}
+
 .sidebar {
        width: 200px;
-       position: fixed;
-       left: 0;
-       top: 0;
-       bottom: 0;
        overflow-y: scroll;
+       position: sticky;
+       min-width: 200px;
+       height: 100vh;
+       top: 0;
+       left: 0;
 }
 
 .rustdoc.source .sidebar {
+       width: 50px;
+       min-width: 0px;
+       max-width: 300px;
+       flex-grow: 0;
+       flex-shrink: 0;
+       flex-basis: auto;
+       border-right: 1px solid;
+       overflow-x: hidden;
+       /* The sidebar is by default hidden  */
+       overflow-y: hidden;
+}
+
+.source .sidebar > *:not(:first-child) {
+       transition: opacity 0.5s, visibility 0.2s;
+       opacity: 0;
+       visibility: hidden;
+}
+
+.source .sidebar.expanded {
        overflow-y: auto;
+       width: 300px;
+}
+
+.source .sidebar.expanded > * {
+       opacity: 1;
+       visibility: visible;
 }
 
 /* Improve the scrollbar display on firefox */
@@ -332,10 +404,6 @@ nav.sub {
        margin-right: -10px;
 }
 
-.content, nav {
-       max-width: 960px;
-}
-
 /* Everything else */
 
 .hidden {
@@ -343,23 +411,15 @@ nav.sub {
 }
 
 .logo-container {
-       height: 100px;
-       width: 100px;
-       position: relative;
-       margin: 20px auto;
-       display: block;
+       display: flex;
        margin-top: 10px;
+       margin-bottom: 10px;
+       justify-content: center;
 }
 
 .logo-container > img {
-       max-width: 100px;
-       max-height: 100px;
-       height: 100%;
-       position: absolute;
-       left: 50%;
-       top: 50%;
-       transform: translate(-50%, -50%);
-       display: block;
+       height: 100px;
+       width: 100px;
 }
 
 .sidebar .location {
@@ -439,10 +499,6 @@ nav.sub {
        display: none;
 }
 
-.content {
-       padding: 15px 0;
-}
-
 .source .content pre.rust {
        white-space: pre;
        overflow: auto;
@@ -487,7 +543,6 @@ nav.sub {
 }
 
 #search {
-       margin-left: 230px;
        position: relative;
 }
 
@@ -578,10 +633,10 @@ nav.sub {
        display: inline-block;
 }
 
-#main {
+#main-content {
        position: relative;
 }
-#main > .since {
+#main-content > .since {
        top: inherit;
        font-family: "Fira Sans", Arial, sans-serif;
 }
@@ -700,14 +755,18 @@ nav.sub {
        flex-basis: 100%;
 }
 
-#main > .item-info {
+#main-content > .item-info {
        margin-top: 0;
 }
 
 nav:not(.sidebar) {
+       flex-grow: 1;
        border-bottom: 1px solid;
        padding-bottom: 10px;
-       margin-bottom: 10px;
+       margin-bottom: 25px;
+}
+.source nav:not(.sidebar).sub {
+       margin-left: 32px;
 }
 nav.main {
        padding: 20px 0;
@@ -726,10 +785,6 @@ nav.main .separator {
 nav.sum { text-align: right; }
 nav.sub form { display: inline; }
 
-nav.sub, .content {
-       margin-left: 230px;
-}
-
 a {
        text-decoration: none;
        background: transparent;
@@ -802,6 +857,7 @@ h2.small-section-header > .anchor {
 
 .search-container {
        position: relative;
+       max-width: 960px;
 }
 .search-container > div {
        display: inline-flex;
@@ -1320,30 +1376,23 @@ pre.rust {
 }
 
 #sidebar-toggle {
-       position: fixed;
-       top: 30px;
-       left: 300px;
-       z-index: 10;
-       padding: 3px;
-       border-top-right-radius: 3px;
-       border-bottom-right-radius: 3px;
+       position: sticky;
+       top: 0;
+       left: 0;
        cursor: pointer;
        font-weight: bold;
-       transition: left .5s;
        font-size: 1.2em;
-       border: 1px solid;
-       border-left: 0;
+       border-bottom: 1px solid;
+       display: flex;
+       height: 40px;
+       justify-content: center;
+       align-items: center;
+       z-index: 10;
 }
 #source-sidebar {
-       position: fixed;
-       top: 0;
-       bottom: 0;
-       left: 0;
        width: 300px;
        z-index: 1;
        overflow: auto;
-       transition: left .5s;
-       border-right: 1px solid;
 }
 #source-sidebar > .title {
        font-size: 1.5em;
@@ -1354,8 +1403,8 @@ pre.rust {
 
 .theme-picker {
        position: absolute;
-       left: 211px;
-       top: 19px;
+       left: -34px;
+       top: 9px;
 }
 
 .theme-picker button {
@@ -1472,10 +1521,10 @@ kbd {
        left: -5px;
 }
 
-#main > ul {
+#main-content > ul {
        padding-left: 10px;
 }
-#main > ul > li {
+#main-content > ul > li {
        list-style: none;
 }
 
@@ -1646,6 +1695,18 @@ details.rustdoc-toggle[open] > summary.hideme::after {
        .docblock > .information:first-child > .tooltip {
                margin-top: 16px;
        }
+
+       /* When we expand the sidebar on the source code page, we hide the logo on the left of the
+       search bar to have more space. */
+       .sidebar.expanded + main .width-limiter .sub-logo-container.rust-logo {
+               display: none;
+       }
+
+       /* It doesn't render well on mobile because of the layout, so better only have the transition
+       on desktop. */
+       .rustdoc.source .sidebar {
+               transition: width .5s;
+       }
 }
 
 @media (max-width: 700px) {
@@ -1653,14 +1714,43 @@ details.rustdoc-toggle[open] > summary.hideme::after {
                padding-top: 0px;
        }
 
-       .rustdoc > .sidebar {
+       main {
+               padding-left: 15px;
+               padding-top: 0px;
+       }
+
+       .rustdoc {
+               flex-direction: column;
+       }
+
+       .rustdoc:not(.source) > .sidebar {
+               width: 100%;
                height: 45px;
                min-height: 40px;
+               max-height: 45px;
                margin: 0;
-               margin-left: -15px;
                padding: 0 15px;
                position: static;
                z-index: 11;
+               overflow-y: hidden;
+       }
+
+       .rustdoc.source > .sidebar {
+               position: fixed;
+               top: 0;
+               left: 0;
+               margin: 0;
+               z-index: 11;
+               width: 0;
+       }
+
+       .sidebar.mobile {
+               position: sticky !important;
+               top: 0;
+               left: 0;
+               width: 100%;
+               margin-left: 0;
+               background-color: rgba(0,0,0,0);
        }
 
        .sidebar > .location {
@@ -1678,7 +1768,7 @@ details.rustdoc-toggle[open] > summary.hideme::after {
                padding: 0;
        }
 
-       .sidebar .logo-container {
+       .rustdoc:not(.source) .sidebar .logo-container {
                width: 35px;
                height: 35px;
                margin-top: 5px;
@@ -1699,6 +1789,7 @@ details.rustdoc-toggle[open] > summary.hideme::after {
                cursor: pointer;
                width: 45px;
                left: 0;
+               top: 0;
                text-align: center;
                display: block;
                border-bottom: 1px solid;
@@ -1748,20 +1839,25 @@ details.rustdoc-toggle[open] > summary.hideme::after {
 
        nav.sub {
                width: calc(100% - 32px);
-               float: right;
+               margin-left: 32px;
+               margin-bottom: 10px;
+       }
+
+       .source nav:not(.sidebar).sub {
+               margin-left: 32px;
        }
 
        .content {
                margin-left: 0px;
        }
 
-       #main, #search {
-               margin-top: 45px;
-               padding: 0;
+       .source .content {
+               margin-top: 10px;
        }
 
        #search {
                margin-left: 0;
+               padding: 0;
        }
 
        .anchor {
@@ -1769,8 +1865,6 @@ details.rustdoc-toggle[open] > summary.hideme::after {
        }
 
        .theme-picker {
-               left: 10px;
-               top: 54px;
                z-index: 1;
        }
 
@@ -1789,25 +1883,6 @@ details.rustdoc-toggle[open] > summary.hideme::after {
                height: 50px;
        }
 
-       .sidebar.mobile {
-               position: fixed;
-               width: 100%;
-               margin-left: 0;
-               background-color: rgba(0,0,0,0);
-               height: 100%;
-       }
-       /*
-       This allows to prevent the version text to overflow the sidebar title on mobile mode when the
-       sidebar is displayed (after clicking on the "hamburger" button).
-       */
-       .sidebar.mobile > div.version {
-               overflow: hidden;
-               max-height: 33px;
-       }
-       .sidebar {
-               width: calc(100% + 30px);
-       }
-
        .show-it, .sidebar-elems:focus-within {
                z-index:  2;
                left: 0;
@@ -1845,8 +1920,8 @@ details.rustdoc-toggle[open] > summary.hideme::after {
                border-bottom: 1px solid;
        }
 
-       #main > details.rustdoc-toggle > summary::before,
-       #main > div > details.rustdoc-toggle > summary::before {
+       #main-content > details.rustdoc-toggle > summary::before,
+       #main-content > div > details.rustdoc-toggle > summary::before {
                left: -11px;
        }
 
@@ -1854,19 +1929,32 @@ details.rustdoc-toggle[open] > summary.hideme::after {
                margin: 10px;
        }
 
-       #sidebar-toggle {
+       .sidebar.expanded #sidebar-toggle {
+               font-size: 1.5rem;
+       }
+
+       .sidebar:not(.expanded) #sidebar-toggle {
+               position: fixed;
+               left: 1px;
                top: 100px;
                width: 30px;
                font-size: 1.5rem;
                text-align: center;
                padding: 0;
+               z-index: 10;
+               border-top-right-radius: 3px;
+               border-bottom-right-radius: 3px;
+               cursor: pointer;
+               font-weight: bold;
+               border: 1px solid;
+               border-left: 0;
        }
 
        #source-sidebar {
                z-index: 11;
        }
 
-       #main > .line-numbers {
+       #main-content > .line-numbers {
                margin-top: 0;
        }
 
@@ -1920,14 +2008,7 @@ details.rustdoc-toggle[open] > summary.hideme::after {
                height: 73px;
        }
 
-       /* This is to prevent the search bar from being underneath the <section>
-        * element following it.
-        */
-       #main, #search {
-               margin-top: 100px;
-       }
-
-       #main > table:not(.table-display) td {
+       #main-content > table:not(.table-display) td {
                word-break: break-word;
                width: 50%;
        }
@@ -1969,6 +2050,23 @@ details.rustdoc-toggle[open] > summary.hideme::after {
        .docblock code {
                overflow-wrap: anywhere;
        }
+
+       .sub-container {
+               flex-direction: column;
+       }
+
+       .sub-logo-container {
+               align-self: center;
+       }
+
+       .source .sub-logo-container > img {
+               height: 35px;
+               width: 35px;
+       }
+
+       .sidebar:not(.expanded) #sidebar-toggle {
+               top: 10px;
+       }
 }
 
 
index 13e8dc85a243ce14857ba8940490e5674153bc81..f4f654f4745268ddb78dc9a270e359898e7846b0 100644 (file)
@@ -61,7 +61,7 @@ pre, .rustdoc.source .example-wrap {
        background-color: #14191f;
 }
 
-.logo-container.rust-logo > img {
+.rust-logo > img {
        filter: drop-shadow(1px 0 0px #fff)
                drop-shadow(0 1px 0 #fff)
                drop-shadow(-1px 0 0 #fff)
@@ -97,7 +97,7 @@ pre, .rustdoc.source .example-wrap {
 }
 
 .source .sidebar {
-       background-color: #0f1419;
+       background-color: #14191f;
 }
 
 .sidebar .location {
index 8caf8a05d507f1eced592b54a77fd1259e39fe3e..b10b80203802938682ff7fe8c56c698b3a86b200 100644 (file)
@@ -32,7 +32,7 @@ pre, .rustdoc.source .example-wrap {
        background-color: #505050;
 }
 
-.logo-container.rust-logo > img {
+.rust-logo > img {
        filter: drop-shadow(1px 0 0px #fff)
                drop-shadow(0 1px 0 #fff)
                drop-shadow(-1px 0 0 #fff)
@@ -66,7 +66,7 @@ pre, .rustdoc.source .example-wrap {
 }
 
 .source .sidebar {
-       background-color: #353535;
+       background-color: #565656;
 }
 
 .sidebar .location {
index fec71674e634f75a56f5758e40f351baac95b1c5..97f32ac1f7de37b47368fc65c091b7d5a0e8437b 100644 (file)
@@ -43,7 +43,7 @@ pre, .rustdoc.source .example-wrap {
        scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9;
 }
 
-.logo-container.rust-logo > img {
+.rust-logo > img {
        /* No need for a border in here! */
 }
 
@@ -66,7 +66,7 @@ pre, .rustdoc.source .example-wrap {
 }
 
 .source .sidebar {
-       background-color: #fff;
+       background-color: #f1f1f1;
 }
 
 .sidebar .location {
index 5661d4973342f034c207b2b9beb725b934fdd80e..411a94ef2d1c18ecac3e86d60b143ef4517915e1 100644 (file)
@@ -94,6 +94,7 @@ function getVirtualKey(ev) {
 
 var THEME_PICKER_ELEMENT_ID = "theme-picker";
 var THEMES_ELEMENT_ID = "theme-choices";
+var MAIN_ID = "main-content";
 
 function getThemesElement() {
     return document.getElementById(THEMES_ELEMENT_ID);
@@ -362,7 +363,7 @@ function hideThemeButtonState() {
     }
 
     var toggleAllDocsId = "toggle-all-docs";
-    var main = document.getElementById("main");
+    var main = document.getElementById(MAIN_ID);
     var savedHash = "";
 
     function handleHashes(ev) {
@@ -787,7 +788,7 @@ function hideThemeButtonState() {
         } else {
             addClass(innerToggle, "will-expand");
             onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function(e) {
-                if (e.parentNode.id !== "main" ||
+                if (e.parentNode.id !== MAIN_ID ||
                     (!hasClass(e, "implementors-toggle") &&
                      !hasClass(e, "type-contents-toggle")))
                 {
@@ -1001,7 +1002,7 @@ function hideThemeButtonState() {
         container.appendChild(rustdoc_version);
 
         popup.appendChild(container);
-        insertAfter(popup, searchState.outputElement());
+        insertAfter(popup, document.querySelector("main"));
         // So that it's only built once and then it'll do nothing when called!
         buildHelperPopup = function() {};
     };
index 4d9a59f836b9e1b0d2604a95dbfe8883ac93111d..81dc0b2fb1eb9d1044c9e32f1af2c1d52845eff1 100644 (file)
@@ -77,16 +77,14 @@ function createDirEntry(elem, parent, fullPath, currentFile, hasFoundFile) {
 }
 
 function toggleSidebar() {
-    var sidebar = document.getElementById("source-sidebar");
-    var child = this.children[0].children[0];
+    var sidebar = document.querySelector("nav.sidebar");
+    var child = this.children[0];
     if (child.innerText === ">") {
-        sidebar.style.left = "";
-        this.style.left = "";
+        sidebar.classList.add("expanded");
         child.innerText = "<";
         updateLocalStorage("rustdoc-source-sidebar-show", "true");
     } else {
-        sidebar.style.left = "-300px";
-        this.style.left = "0";
+        sidebar.classList.remove("expanded");
         child.innerText = ">";
         updateLocalStorage("rustdoc-source-sidebar-show", "false");
     }
@@ -97,20 +95,15 @@ function createSidebarToggle() {
     sidebarToggle.id = "sidebar-toggle";
     sidebarToggle.onclick = toggleSidebar;
 
-    var inner1 = document.createElement("div");
-    inner1.style.position = "relative";
+    var inner = document.createElement("div");
 
-    var inner2 = document.createElement("div");
-    inner2.style.paddingTop = "3px";
     if (getCurrentValue("rustdoc-source-sidebar-show") === "true") {
-        inner2.innerText = "<";
+        inner.innerText = "<";
     } else {
-        inner2.innerText = ">";
-        sidebarToggle.style.left = "0";
+        inner.innerText = ">";
     }
 
-    inner1.appendChild(inner2);
-    sidebarToggle.appendChild(inner1);
+    sidebarToggle.appendChild(inner);
     return sidebarToggle;
 }
 
@@ -120,15 +113,17 @@ function createSourceSidebar() {
     if (!window.rootPath.endsWith("/")) {
         window.rootPath += "/";
     }
-    var main = document.getElementById("main");
+    var container = document.querySelector("nav.sidebar");
 
     var sidebarToggle = createSidebarToggle();
-    main.insertBefore(sidebarToggle, main.firstChild);
+    container.insertBefore(sidebarToggle, container.firstChild);
 
     var sidebar = document.createElement("div");
     sidebar.id = "source-sidebar";
     if (getCurrentValue("rustdoc-source-sidebar-show") !== "true") {
-        sidebar.style.left = "-300px";
+        container.classList.remove("expanded");
+    } else {
+        container.classList.add("expanded");
     }
 
     var currentFile = getCurrentFilePath();
@@ -144,7 +139,7 @@ function createSourceSidebar() {
                                       currentFile, hasFoundFile);
     });
 
-    main.insertBefore(sidebar, main.firstChild);
+    container.insertBefore(sidebar, document.querySelector(".sidebar-logo").nextSibling);
     // Focus on the current file in the source files sidebar.
     var selected_elem = sidebar.getElementsByClassName("selected")[0];
     if (typeof selected_elem !== "undefined") {
index c07851da18ac1238c308a50d876ea5a9f86a160a..3c6f083419aefbf038ffd52e86c843b964c0fb4e 100644 (file)
     {{- layout.external_html.before_content | safe -}}
     <nav class="sidebar"> {#- -#}
         <div class="sidebar-menu" role="button">&#9776;</div> {#- -#}
-        <a href='{{page.root_path | safe}}{{krate_with_trailing_slash | safe}}index.html'> {#- -#}
+        <a class="sidebar-logo" href='{{page.root_path | safe}}{{krate_with_trailing_slash | safe}}index.html'> {#- -#}
             <div class='logo-container rust-logo'> {#- -#}
-            <img src='
-                {%- if layout.logo -%}
-                {{layout.logo}}
-                {%- else -%}
-                {{static_root_path | safe}}rust-logo{{page.resource_suffix}}.png
-                {%- endif -%}
-                ' alt='logo'> {#- -#}
+                <img src='
+                    {%- if layout.logo -%}
+                    {{layout.logo}}
+                    {%- else -%}
+                    {{static_root_path | safe}}rust-logo{{page.resource_suffix}}.png
+                    {%- endif -%}
+                    ' alt='logo'> {#- -#}
             </div> {#- -#}
         </a> {#- -#}
         {{- sidebar | safe -}}
     </nav> {#- -#}
-    <div class="theme-picker"> {#- -#}
-        <button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu" title="themes"> {#- -#}
-            <img width="18" height="18" alt="Pick another theme!" {# -#}
-             src="{{static_root_path | safe}}brush{{page.resource_suffix}}.svg"> {#- -#}
-        </button> {#- -#}
-        <div id="theme-choices" role="menu"></div> {#- -#}
-    </div> {#- -#}
-    <nav class="sub"> {#- -#}
-        <form class="search-form"> {#- -#}
-            <div class="search-container"> {#- -#}
-                <div>{%- if layout.generate_search_filter -%}
-                    <select id="crate-search"> {#- -#}
-                        <option value="All crates">All crates</option> {#- -#}
-                    </select> {#- -#}
-                    {%- endif -%}
-                    <input {# -#}
-                        class="search-input" {# -#}
-                        name="search" {# -#}
-                        autocomplete="off" {# -#}
-                        spellcheck="false" {# -#}
-                        placeholder="Click or press ‘S’ to search, ‘?’ for more options…" {# -#}
-                        type="search"> {#- -#}
-                </div> {#- -#}
-                <button type="button" id="help-button" title="help">?</button> {#- -#}
-                <a id="settings-menu" href="{{page.root_path | safe}}settings.html" title="settings"> {#- -#}
-                    <img width="18" height="18" alt="Change settings" {# -#}
-                         src="{{static_root_path | safe}}wheel{{page.resource_suffix}}.svg"> {#- -#}
+    <main> {#- -#}
+        <div class="width-limiter"> {#- -#}
+            <div class="sub-container"> {#- -#}
+                <a class="sub-logo-container rust-logo" href='{{page.root_path | safe}}{{krate_with_trailing_slash | safe}}index.html'> {#- -#}
+                    <img src='
+                        {%- if layout.logo -%}
+                        {{layout.logo}}
+                        {%- else -%}
+                        {{static_root_path | safe}}rust-logo{{page.resource_suffix}}.png
+                        {%- endif -%}
+                        ' alt='logo'> {#- -#}
                 </a> {#- -#}
+                <nav class="sub"> {#- -#}
+                    <div class="theme-picker"> {#- -#}
+                        <button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu" title="themes"> {#- -#}
+                            <img width="18" height="18" alt="Pick another theme!" {# -#}
+                             src="{{static_root_path | safe}}brush{{page.resource_suffix}}.svg"> {#- -#}
+                        </button> {#- -#}
+                        <div id="theme-choices" role="menu"></div> {#- -#}
+                    </div> {#- -#}
+                    <form class="search-form"> {#- -#}
+                        <div class="search-container"> {#- -#}
+                            <div>{%- if layout.generate_search_filter -%}
+                                <select id="crate-search"> {#- -#}
+                                    <option value="All crates">All crates</option> {#- -#}
+                                </select> {#- -#}
+                                {%- endif -%}
+                                <input {# -#}
+                                    class="search-input" {# -#}
+                                    name="search" {# -#}
+                                    autocomplete="off" {# -#}
+                                    spellcheck="false" {# -#}
+                                    placeholder="Click or press ‘S’ to search, ‘?’ for more options…" {# -#}
+                                    type="search"> {#- -#}
+                            </div> {#- -#}
+                            <button type="button" id="help-button" title="help">?</button> {#- -#}
+                            <a id="settings-menu" href="{{page.root_path | safe}}settings.html" title="settings"> {#- -#}
+                                <img width="18" height="18" alt="Change settings" {# -#}
+                                     src="{{static_root_path | safe}}wheel{{page.resource_suffix}}.svg"> {#- -#}
+                            </a> {#- -#}
+                        </div> {#- -#}
+                    </form> {#- -#}
+                </nav> {#- -#}
             </div> {#- -#}
-        </form> {#- -#}
-    </nav> {#- -#}
-    <section id="main" class="content">{{- content | safe -}}</section> {#- -#}
-    <section id="search" class="content hidden"></section> {#- -#}
+            <section id="main-content" class="content">{{- content | safe -}}</section> {#- -#}
+            <section id="search" class="content hidden"></section> {#- -#}
+        </div> {#- -#}
+    </main> {#- -#}
     {{- layout.external_html.after_content | safe -}}
     <div id="rustdoc-vars" {# -#}
          data-root-path="{{page.root_path | safe}}" {# -#}
diff --git a/src/test/assembly/asm/avr-modifiers.rs b/src/test/assembly/asm/avr-modifiers.rs
new file mode 100644 (file)
index 0000000..aba4c98
--- /dev/null
@@ -0,0 +1,60 @@
+// min-llvm-version: 13.0
+// assembly-output: emit-asm
+// compile-flags: --target avr-unknown-gnu-atmega328
+// needs-llvm-components: avr
+
+#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)]
+#![crate_type = "rlib"]
+#![no_core]
+#![allow(non_camel_case_types)]
+
+#[rustc_builtin_macro]
+macro_rules! asm {
+    () => {};
+}
+#[rustc_builtin_macro]
+macro_rules! concat {
+    () => {};
+}
+
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+
+type ptr = *const u64;
+
+impl Copy for i8 {}
+impl Copy for i16 {}
+impl Copy for i32 {}
+impl Copy for i64 {}
+impl Copy for ptr {}
+
+macro_rules! check {
+    ($func:ident $hi:literal $lo:literal $reg:tt) => {
+        #[no_mangle]
+        unsafe fn $func() -> i16 {
+            let y;
+            asm!(concat!("mov {0:", $hi, "}, {0:", $lo, "}"), out($reg) y);
+            y
+        }
+    };
+}
+
+// CHECK-LABEL: reg_pair_modifiers:
+// CHECK: ;APP
+// CHECK: mov r{{[1-9]?[13579]}}, r{{[1-9]?[24680]}}
+// CHECK: ;NO_APP
+check!(reg_pair_modifiers "h" "l" reg_pair);
+
+// CHECK-LABEL: reg_iw_modifiers:
+// CHECK: ;APP
+// CHECK: mov r{{[1-9]?[13579]}}, r{{[1-9]?[24680]}}
+// CHECK: ;NO_APP
+check!(reg_iw_modifiers "h" "l" reg_iw);
+
+// CHECK-LABEL: reg_ptr_modifiers:
+// CHECK: ;APP
+// CHECK: mov r{{[1-9]?[13579]}}, r{{[1-9]?[24680]}}
+// CHECK: ;NO_APP
+check!(reg_ptr_modifiers "h" "l" reg_ptr);
diff --git a/src/test/assembly/asm/avr-types.rs b/src/test/assembly/asm/avr-types.rs
new file mode 100644 (file)
index 0000000..53a601e
--- /dev/null
@@ -0,0 +1,222 @@
+// min-llvm-version: 13.0
+// assembly-output: emit-asm
+// compile-flags: --target avr-unknown-gnu-atmega328
+// needs-llvm-components: avr
+
+#![feature(no_core, lang_items, rustc_attrs, asm_sym, asm_experimental_arch)]
+#![crate_type = "rlib"]
+#![no_core]
+#![allow(non_camel_case_types)]
+
+#[rustc_builtin_macro]
+macro_rules! asm {
+    () => {};
+}
+#[rustc_builtin_macro]
+macro_rules! concat {
+    () => {};
+}
+
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+
+type ptr = *const u64;
+
+impl Copy for i8 {}
+impl Copy for i16 {}
+impl Copy for i32 {}
+impl Copy for i64 {}
+impl Copy for ptr {}
+
+macro_rules! check {
+    ($func:ident $ty:ident $class:ident) => {
+        #[no_mangle]
+        pub unsafe fn $func(x: $ty) -> $ty {
+            let y;
+            asm!("mov {}, {}", lateout($class) y, in($class) x);
+            y
+        }
+    };
+}
+
+macro_rules! checkw {
+    ($func:ident $ty:ident $class:ident) => {
+        #[no_mangle]
+        pub unsafe fn $func(x: $ty) -> $ty {
+            let y;
+            asm!("movw {}, {}", lateout($class) y, in($class) x);
+            y
+        }
+    };
+}
+
+macro_rules! check_reg {
+    ($func:ident $ty:ident $reg:tt) => {
+        #[no_mangle]
+        pub unsafe fn $func(x: $ty) -> $ty {
+            let y;
+            asm!(concat!("mov ", $reg, ", ", $reg), lateout($reg) y, in($reg) x);
+            y
+        }
+    };
+}
+
+macro_rules! check_regw {
+    ($func:ident $ty:ident $reg:tt $reg_lit:tt) => {
+        #[no_mangle]
+        pub unsafe fn $func(x: $ty) -> $ty {
+            let y;
+            asm!(concat!("movw ", $reg_lit, ", ", $reg_lit), lateout($reg) y, in($reg) x);
+            y
+        }
+    };
+}
+
+extern "C" {
+    fn extern_func();
+    static extern_static: i8;
+}
+
+// CHECK-LABEL: sym_fn
+// CHECK: ;APP
+// CHECK: call extern_func
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn sym_fn() {
+    asm!("call {}", sym extern_func);
+}
+
+// CHECK-LABEL: sym_static
+// CHECK: ;APP
+// CHECK: lds r{{[0-9]+}}, extern_static
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn sym_static() -> i8 {
+    let y;
+    asm!("lds {}, {}", lateout(reg) y, sym extern_static);
+    y
+}
+
+// CHECK-LABEL: ld_z:
+// CHECK: ;APP
+// CHECK: ld r{{[0-9]+}}, Z
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn ld_z(x: i16) -> i8 {
+    let y;
+    asm!("ld {}, Z", out(reg) y, in("Z") x);
+    y
+}
+
+// CHECK-LABEL: ldd_z:
+// CHECK: ;APP
+// CHECK: ldd r{{[0-9]+}}, Z+4
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn ldd_z(x: i16) -> i8 {
+    let y;
+    asm!("ldd {}, Z+4", out(reg) y, in("Z") x);
+    y
+}
+
+// CHECK-LABEL: ld_predecrement:
+// CHECK: ;APP
+// CHECK: ld r{{[0-9]+}}, -Z
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn ld_predecrement(x: i16) -> i8 {
+    let y;
+    asm!("ld {}, -Z", out(reg) y, in("Z") x);
+    y
+}
+
+// CHECK-LABEL: ld_postincrement:
+// CHECK: ;APP
+// CHECK: ld r{{[0-9]+}}, Z+
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn ld_postincrement(x: i16) -> i8 {
+    let y;
+    asm!("ld {}, Z+", out(reg) y, in("Z") x);
+    y
+}
+
+// CHECK-LABEL: muls_clobber:
+// CHECK: ;APP
+// CHECK: muls r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: movw r{{[0-9]+}}, r0
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn muls_clobber(x: i8, y: i8) -> i16 {
+    let z;
+    asm!(
+        "muls {}, {}",
+        "movw {}, r1:r0",
+        out(reg_iw) z,
+        in(reg) x,
+        in(reg) y,
+    );
+    z
+}
+
+// CHECK-LABEL: reg_i8:
+// CHECK: ;APP
+// CHECK: mov r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: ;NO_APP
+check!(reg_i8 i8 reg);
+
+// CHECK-LABEL: reg_upper_i8:
+// CHECK: ;APP
+// CHECK: mov r{{[1-3][0-9]}}, r{{[1-3][0-9]}}
+// CHECK: ;NO_APP
+check!(reg_upper_i8 i8 reg_upper);
+
+// CHECK-LABEL: reg_pair_i16:
+// CHECK: ;APP
+// CHECK: movw r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: ;NO_APP
+checkw!(reg_pair_i16 i16 reg_pair);
+
+// CHECK-LABEL: reg_iw_i16:
+// CHECK: ;APP
+// CHECK: movw r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: ;NO_APP
+checkw!(reg_iw_i16 i16 reg_iw);
+
+// CHECK-LABEL: reg_ptr_i16:
+// CHECK: ;APP
+// CHECK: movw r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: ;NO_APP
+checkw!(reg_ptr_i16 i16 reg_ptr);
+
+// CHECK-LABEL: r2_i8:
+// CHECK: ;APP
+// CHECK: mov r2, r2
+// CHECK: ;NO_APP
+check_reg!(r2_i8 i8 "r2");
+
+// CHECK-LABEL: xl_i8:
+// CHECK: ;APP
+// CHECK: mov r26, r26
+// CHECK: ;NO_APP
+check_reg!(xl_i8 i8 "XL");
+
+// CHECK-LABEL: xh_i8:
+// CHECK: ;APP
+// CHECK: mov r27, r27
+// CHECK: ;NO_APP
+check_reg!(xh_i8 i8 "XH");
+
+// CHECK-LABEL: x_i16:
+// CHECK: ;APP
+// CHECK: movw r26, r26
+// CHECK: ;NO_APP
+check_regw!(x_i16 i16 "X" "X");
+
+// CHECK-LABEL: r25r24_i16:
+// CHECK: ;APP
+// CHECK: movw r24, r24
+// CHECK: ;NO_APP
+check_regw!(r25r24_i16 i16 "r25r24" "r24");
diff --git a/src/test/incremental/issue-85360-eval-obligation-ice.rs b/src/test/incremental/issue-85360-eval-obligation-ice.rs
new file mode 100644 (file)
index 0000000..1796c9d
--- /dev/null
@@ -0,0 +1,118 @@
+// revisions:cfail1 cfail2
+//[cfail1] compile-flags: --crate-type=lib --edition=2021 -Zassert-incr-state=not-loaded
+//[cfail2] compile-flags: --crate-type=lib --edition=2021 -Zassert-incr-state=loaded
+// build-pass
+
+use core::any::Any;
+use core::marker::PhantomData;
+
+struct DerefWrap<T>(T);
+
+impl<T> core::ops::Deref for DerefWrap<T> {
+    type Target = T;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+struct Storage<T, D> {
+    phantom: PhantomData<(T, D)>,
+}
+
+type ReadStorage<T> = Storage<T, DerefWrap<MaskedStorage<T>>>;
+
+pub trait Component {
+    type Storage;
+}
+
+struct VecStorage;
+
+struct Pos;
+
+impl Component for Pos {
+    type Storage = VecStorage;
+}
+
+struct GenericComp<T> {
+    _t: T,
+}
+
+impl<T: 'static> Component for GenericComp<T> {
+    type Storage = VecStorage;
+}
+struct ReadData {
+    pos_interpdata: ReadStorage<GenericComp<Pos>>,
+}
+
+trait System {
+    type SystemData;
+
+    fn run(data: Self::SystemData, any: Box<dyn Any>);
+}
+
+struct Sys;
+
+impl System for Sys {
+    type SystemData = (ReadData, ReadStorage<Pos>);
+
+    fn run((data, pos): Self::SystemData, any: Box<dyn Any>) {
+        <ReadStorage<GenericComp<Pos>> as SystemData>::setup(any);
+
+        ParJoin::par_join((&pos, &data.pos_interpdata));
+    }
+}
+
+trait ParJoin {
+    fn par_join(self)
+    where
+        Self: Sized,
+    {
+    }
+}
+
+impl<'a, T, D> ParJoin for &'a Storage<T, D>
+where
+    T: Component,
+    D: core::ops::Deref<Target = MaskedStorage<T>>,
+    T::Storage: Sync,
+{
+}
+
+impl<A, B> ParJoin for (A, B)
+where
+    A: ParJoin,
+    B: ParJoin,
+{
+}
+
+pub trait SystemData {
+    fn setup(any: Box<dyn Any>);
+}
+
+impl<T: 'static> SystemData for ReadStorage<T>
+where
+    T: Component,
+{
+    fn setup(any: Box<dyn Any>) {
+        let storage: &MaskedStorage<T> = any.downcast_ref().unwrap();
+
+        <dyn Any as CastFrom<MaskedStorage<T>>>::cast(&storage);
+    }
+}
+
+pub struct MaskedStorage<T: Component> {
+    _inner: T::Storage,
+}
+
+pub unsafe trait CastFrom<T> {
+    fn cast(t: &T) -> &Self;
+}
+
+unsafe impl<T> CastFrom<T> for dyn Any
+where
+    T: Any + 'static,
+{
+    fn cast(t: &T) -> &Self {
+        t
+    }
+}
index 32d1da390c547728e3f0efb5f32f8db16e492af6..c80bce2d140b9228df0a755509d450404fbf6d0b 100644 (file)
@@ -1,6 +1,6 @@
 // pp-exact
 
-fn main() { }
+fn main() {}
 
 #[cfg(FALSE)]
 fn syntax() {
@@ -117,7 +117,7 @@ fn syntax() {
     let _ = #[attr] foo!(#! [attr]);
     let _ = #[attr] foo![];
     let _ = #[attr] foo![#! [attr]];
-    let _ = #[attr] foo! { };
+    let _ = #[attr] foo! {};
     let _ = #[attr] foo! { #! [attr] };
     let _ = #[attr] Foo{bar: baz,};
     let _ = #[attr] Foo{..foo};
@@ -135,7 +135,7 @@ fn syntax() {
         foo!();
 
         #[attr]
-        foo! { }
+        foo! {}
 
         #[attr]
         foo![];
@@ -170,6 +170,6 @@ fn syntax() {
     {
 
         #[attr]
-        foo! { }
+        foo! {}
     }
 }
diff --git a/src/test/pretty/async.rs b/src/test/pretty/async.rs
new file mode 100644 (file)
index 0000000..573e79b
--- /dev/null
@@ -0,0 +1,9 @@
+// pp-exact
+// pretty-compare-only
+// edition:2021
+
+async fn f() {
+    let first = async { 1 };
+    let second = async move { 2 };
+    join(first, second).await
+}
index 4ce6d2fbf17b9265cb18d151e5710494c0b5c471..0eb403c6bc843aeb4703cdf708a0e442200caad8 100644 (file)
@@ -29,4 +29,4 @@ enum Enum {
     Qwerty,
 }
 
-fn main() { }
+fn main() {}
index 61644e7df09ae5f64304666f7ed1c82b3d637906..c3c47cff5eda9a600dfcff80fab4aa10956d0502 100644 (file)
@@ -2,8 +2,8 @@
 
 // pp-exact
 
-auto trait MyTrait { }
+auto trait MyTrait {}
 
-unsafe auto trait UnsafeMyTrait { }
+unsafe auto trait UnsafeMyTrait {}
 
-pub fn main() { }
+pub fn main() {}
index b7c42e39557c9cd89f665dd692e6d13a297fedf5..e53d51e34cefa8d3942eb09f8c52c7d0d1319f3b 100644 (file)
@@ -1,9 +1,8 @@
 // compile-flags: --crate-type=lib
 
 // pp-exact
-fn f() {
-} /*
-  The next line should not be indented.
+fn f() {} /*
+          The next line should not be indented.
 
-  That one. It shouldn't have been indented.
-  */
+          That one. It shouldn't have been indented.
+          */
index ef5189fa13ee1c6a62880572eb39ea32eaa7027e..1eba1e6bdec3f4f0507d606b975d9628ff4b71b0 100644 (file)
@@ -3,14 +3,14 @@
 
 // pp-exact
 
-fn call_it(f: Box<FnMut(String) -> String>) { }
+fn call_it(f: Box<FnMut(String) -> String>) {}
 
-fn call_this<F>(f: F) where F: Fn(&str) + Send { }
+fn call_this<F>(f: F) where F: Fn(&str) + Send {}
 
-fn call_that<F>(f: F) where F: for<'a> Fn(&'a isize, &'a isize) -> isize { }
+fn call_that<F>(f: F) where F: for<'a> Fn(&'a isize, &'a isize) -> isize {}
 
-fn call_extern(f: fn() -> isize) { }
+fn call_extern(f: fn() -> isize) {}
 
-fn call_abid_extern(f: extern "C" fn() -> isize) { }
+fn call_abid_extern(f: extern "C" fn() -> isize) {}
 
-pub fn main() { }
+pub fn main() {}
index 601ca7bb6de1cd3e6836cf1cd6da5854d9328ba2..734f9fa123eea2c420947d12f3c162b92f7aa5a4 100644 (file)
@@ -7,4 +7,4 @@
 fn id<F>(f: F) -> isize where F: Fn() -> isize { f() }
 
 fn wsucc(_n: isize) -> isize { id(|| { 1 }) - 0 }
-fn main() { }
+fn main() {}
index a3e8178aeb3ded4cd2a98ef3d8b7c1ed2ee679b5..fc646c2956c4f74b8e25c920cac036c7b91a9988 100644 (file)
@@ -2,7 +2,7 @@
 
 // Check that the visibility is printed on an enum variant.
 
-fn main() { }
+fn main() {}
 
 #[cfg(FALSE)]
 enum Foo { pub V, }
index 4f7d5e7e71e176d161d26f1f7dbdd24fd686154b..8df74e8e1f9c871247264f335b576916915b616e 100644 (file)
@@ -1,3 +1,3 @@
 // pp-exact
 
-fn main() { }
+fn main() {}
index 852b9f316ce0fca5bf0662b3136f406d01abd30e..3f7129afde26d3c5f0530da24e950b745cee84a4 100644 (file)
@@ -1,3 +1,3 @@
 // pp-exact:example2.pp
 
-fn main() { }
+fn main() {}
index 852b9f316ce0fca5bf0662b3136f406d01abd30e..3f7129afde26d3c5f0530da24e950b745cee84a4 100644 (file)
@@ -1,3 +1,3 @@
 // pp-exact:example2.pp
 
-fn main() { }
+fn main() {}
index 1579ea41cfdf1b62ad4fb4f735505e39efff1c67..8385c5fa8c91a75d30224568e3f1852754ef2899 100644 (file)
@@ -10,4 +10,4 @@ extern crate std;
 // pp-exact:expanded-and-path-remap-80832.pp
 // compile-flags: --remap-path-prefix {{src-base}}=the/src
 
-fn main() { }
+fn main() {}
index 142d852cd6a132326deaadea46423c39079acf47..b932e83aaf10fd7f0a5e0bbc56abb1659771cab3 100644 (file)
@@ -1,7 +1,7 @@
 // pp-exact
 
-// Check that `fn f() -> () { }` does not print as `fn f() { }`.
+// Check that `fn f() -> () {}` does not print as `fn f() {}`.
 
-fn f() -> () { }
+fn f() -> () {}
 
-fn main() { }
+fn main() {}
index 842138e28c3531f51baa001980e3c07bd7123492..f012763c3f606f61cc04615f1c953c7dbd9c680e 100644 (file)
@@ -1,5 +1,5 @@
 // pp-exact
 
-fn from_foreign_fn(_x: fn()) { }
-fn from_stack_closure<F>(_x: F) where F: Fn() { }
-fn main() { }
+fn from_foreign_fn(_x: fn()) {}
+fn from_stack_closure<F>(_x: F) where F: Fn() {}
+fn main() {}
index d499be424603badfa5f9fd4c4a70dd6ae7432661..59e477cfa8ecba76fa24692ccdad2ad0331802ec 100644 (file)
@@ -12,4 +12,4 @@
     ap.arg::<usize>()
 }
 
-fn main() { }
+fn main() {}
index 652604fc7f34b2808e6af7db558ab27189ab332e..7b90b0becacbaf38e3b1dc2e8b4ae624489564f6 100644 (file)
@@ -4,34 +4,34 @@
 fn simple_attr() {
 
     #[attr]
-    if true { }
+    if true {}
 
     #[allow_warnings]
-    if true { }
+    if true {}
 }
 
 #[cfg(FALSE)]
 fn if_else_chain() {
 
     #[first_attr]
-    if true { } else if false { } else { }
+    if true {} else if false {} else {}
 }
 
 #[cfg(FALSE)]
 fn if_let() {
 
     #[attr]
-    if let Some(_) = Some(true) { }
+    if let Some(_) = Some(true) {}
 }
 
 #[cfg(FALSE)]
 fn let_attr_if() {
-    let _ = #[attr] if let _ = 0 { };
-    let _ = #[attr] if true { };
+    let _ = #[attr] if let _ = 0 {};
+    let _ = #[attr] if true {};
 
-    let _ = #[attr] if let _ = 0 { } else { };
-    let _ = #[attr] if true { } else { };
+    let _ = #[attr] if let _ = 0 {} else {};
+    let _ = #[attr] if true {} else {};
 }
 
 
-fn main() { }
+fn main() {}
index ca1fef83cffc5fcfa249e8496d39d6c79fd71456..3c88f5cb8a41c13012e883c78fb847d49687b77b 100644 (file)
@@ -6,4 +6,4 @@
 #[path = "issue-12590-b.rs"]
 mod issue_12590_b;
 
-fn main() { }
+fn main() {}
index dd0b8899b2d9d5ee57d4a8aa2bbf3c00199be497..07b3f5653d36b076eddccde7dd4afd0189fbcb91 100644 (file)
@@ -13,7 +13,7 @@ extern crate std;
 #[path = "issue-12590-b.rs"]
 mod issue_12590_b {
 
-    fn b() { }
-    fn main() { }
+    fn b() {}
+    fn main() {}
 }
-fn main() { }
+fn main() {}
index 2cc444edda3d7f1d0900add319b5dbe7703ffe98..0ec05f9a8051ba6e1f87471c91c1b5b8466756b6 100644 (file)
@@ -7,4 +7,4 @@
 #[path = "issue-12590-b.rs"]
 mod issue_12590_b;
 
-fn main() { }
+fn main() {}
index b42791113056e336c354142ab33814b1466f4993..0d2702804d12db379962dbf2ccea1cde7c33d5d5 100644 (file)
@@ -4,8 +4,8 @@
 fn main() {
     match true {
         true if true => (),
-        false if false => unsafe { },
-        true => { }
+        false if false => unsafe {},
+        true => {}
         false => (),
     }
 }
index 02951395e70b440491cab26889ef5c1301bda97c..607cbebee170c124737f0034f58ca2ad7f0a1fce 100644 (file)
@@ -5,4 +5,4 @@
 // pretty-compare-only
 // pp-exact
 
-fn main() { b! { } c }
+fn main() { b! {} c }
index 031a4825959329f11360f13940057b1c8ebcac31..ed7879001d5598df74797deeb9d6ac73db3a5b01 100644 (file)
@@ -1,6 +1,6 @@
 // pp-exact
 
-fn main() { }
+fn main() {}
 
 struct C {
     field: u8,
index bfef51202dbe1f81db412b1c9c0632e900b47c6f..34eae849be45eab5f215752a5884023699a23ae5 100644 (file)
@@ -1,5 +1,5 @@
 // pp-exact
 
-fn f1<'a, 'b, 'c>(_x: &'a u32, _y: &'b u32, _z: &'c u32) where 'c: 'a + 'b { }
+fn f1<'a, 'b, 'c>(_x: &'a u32, _y: &'b u32, _z: &'c u32) where 'c: 'a + 'b {}
 
-fn main() { }
+fn main() {}
index d3865d93a30d8ca0949c0808a39f0aeded46aa11..b88ae703950baec310a26f0deeee62a05372c0f1 100644 (file)
@@ -4,4 +4,4 @@
 
 pub(crate) macro mac { ($arg : expr) => { $arg + $arg } }
 
-fn main() { }
+fn main() {}
index 3b13f2530dd9276f2009948de758835e9c6bd63c..fb66e4a7758420962926977cb859dcfc997243b5 100644 (file)
@@ -1,19 +1,19 @@
 // pp-exact
 
-macro_rules! brace { () => { } ; }
+macro_rules! brace { () => {} ; }
 
-macro_rules! bracket[() => { } ;];
+macro_rules! bracket[() => {} ;];
 
-macro_rules! paren(() => { } ;);
+macro_rules! paren(() => {} ;);
 
 macro_rules! matcher_brackets {
-    (paren) => { } ; (bracket) => { } ; (brace) => { } ;
+    (paren) => {} ; (bracket) => {} ; (brace) => {} ;
 }
 
 macro_rules! all_fragments {
     ($b : block, $e : expr, $i : ident, $it : item, $l : lifetime, $lit :
      literal, $m : meta, $p : pat, $pth : path, $s : stmt, $tt : tt, $ty : ty,
-     $vis : vis) => { } ;
+     $vis : vis) => {} ;
 }
 
-fn main() { }
+fn main() {}
index 0a3f2a10c8549ab24fa354baaad7361acc9865d0..f46c0e3f1bc629fe38753079d6e1672d5c282066 100644 (file)
@@ -2,7 +2,7 @@
 
 // pp-exact
 
-fn main() { }
+fn main() {}
 
 #[cfg(FALSE)]
 extern "C" {
index 3072e416cbeefdb71067c8dd42f8d898ea2163c2..f1970de6feb4b14581aa65c4f7fc4a55195e41b8 100644 (file)
@@ -2,9 +2,9 @@
 
 
 trait Tr {
-    fn dummy(&self) { }
+    fn dummy(&self) {}
 }
-impl Tr for isize { }
+impl Tr for isize {}
 
 fn foo<'a>(x: Box<Tr + Sync + 'a>) -> Box<Tr + Sync + 'a> { x }
 
index e06885e03882bc6d5d99a79b76c1d3b8b51a9b4e..67a5d1dd8ec664db2d54205a7fa1d3a8202e2312 100644 (file)
@@ -8,9 +8,9 @@ pub trait Tr {
 }
 
 trait Tu {
-    fn dummy() { }
+    fn dummy() {}
 }
 
 fn foo<T: m::Tr>() { <T as m::Tr>::Ts::dummy(); }
 
-fn main() { }
+fn main() {}
index e4a3acade871be7e443fb85e71d3da1dba43233b..12204c8cd30ed825e454c8f314aa04a468ea1250 100644 (file)
@@ -4,7 +4,7 @@
 #![feature(rustc_attrs)]
 #![feature(stmt_expr_attributes)]
 
-fn main() { }
+fn main() {}
 
 fn _0() {
 
@@ -35,7 +35,7 @@ fn _2() {
 fn _3() {
 
     #[rustc_dummy]
-    match () { _ => { } }
+    match () { _ => {} }
 }
 
 fn _4() {
@@ -117,13 +117,13 @@ fn _9() {
     stmt_mac!();
 
     #[rustc_dummy]
-    stmt_mac! { };
+    stmt_mac! {};
 
     #[rustc_dummy]
     stmt_mac![];
 
     #[rustc_dummy]
-    stmt_mac! { }
+    stmt_mac! {}
 
     let _ = ();
 }
@@ -133,7 +133,7 @@ macro_rules! expr_mac { () => { () } }
 fn _10() {
     let _ = #[rustc_dummy] expr_mac!();
     let _ = #[rustc_dummy] expr_mac![];
-    let _ = #[rustc_dummy] expr_mac! { };
+    let _ = #[rustc_dummy] expr_mac! {};
 }
 
 fn _11() {
@@ -235,7 +235,7 @@ fn _11() {
     || #[rustc_dummy] return;
     let _ = #[rustc_dummy] expr_mac!();
     let _ = #[rustc_dummy] expr_mac![];
-    let _ = #[rustc_dummy] expr_mac! { };
+    let _ = #[rustc_dummy] expr_mac! {};
     let _ = #[rustc_dummy] Foo{data: (),};
     let _ = #[rustc_dummy] Foo{..s};
     let _ = #[rustc_dummy] Foo{data: (), ..s};
@@ -258,6 +258,6 @@ fn _12() {
     }
 }
 
-fn foo() { }
-fn foo3(_: i32, _: (), _: ()) { }
-fn qux(_: i32) { }
+fn foo() {}
+fn foo3(_: i32, _: (), _: ()) {}
+fn qux(_: i32) {}
index 28ed4fccaf26be8bd328bfb3803a227fdbfdf078..d53f6e4b52850b8a6d0b9317f64b5bc6c8c5ed24 100644 (file)
@@ -5,4 +5,4 @@ enum foo {
     baz,
 }
 
-fn main() { }
+fn main() {}
index bb4fb1459bd64597037bb9afad15ac31bb73bec0..6cb0e4136b62cc2eddfe3f8ca44303010823d62c 100644 (file)
@@ -4,4 +4,4 @@ trait Foo {
     #![allow(bar)]
 }
 
-fn main() { }
+fn main() {}
index df1a7946afb119c3cf89fac4bcbd8322d907ce90..310506eabca17a2250833a99b89b7ee10d8af02d 100644 (file)
@@ -4,6 +4,6 @@
 
 struct Test;
 
-impl !Send for Test { }
+impl !Send for Test {}
 
-pub fn main() { }
+pub fn main() {}
index b2f2d610c31228c2de294f5726420c417f58b976..c4ae7606946badce71e41784f8afd4ca3b6ef754 100644 (file)
@@ -5,11 +5,11 @@ unsafe trait UnsafeTrait {
 }
 
 unsafe impl UnsafeTrait for isize {
-    fn foo(&self) { }
+    fn foo(&self) {}
 }
 
 pub unsafe trait PubUnsafeTrait {
     fn foo(&self);
 }
 
-fn main() { }
+fn main() {}
index 3d6e431a11a9afae97be44dee12fc259286bb190..5614a81b0eb410f0d7afe97ed090e244a296428f 100644 (file)
@@ -2,4 +2,4 @@
 
 fn f<'a, 'b, T>(t: T) -> isize where T: 'a, 'a: 'b, T: Eq { 0 }
 
-fn main() { }
+fn main() {}
index 2de47682856796c44180460e4633ffac22e94289..f6266eba75de7703932366e0e27d0f64b6bb623d 100644 (file)
@@ -1,6 +1,6 @@
 // This test ensures that the docblock elements have the appropriate left margin.
 goto: file://|DOC_PATH|/test_docs/fn.foo.html
 // The top docblock elements shouldn't have left margin...
-assert-css: ("#main .docblock.item-decl", {"margin-left": "0px"})
+assert-css: ("#main-content .docblock.item-decl", {"margin-left": "0px"})
 // ... but all the others should!
-assert-css: ("#main .docblock:not(.item-decl)", {"margin-left": "24px"})
+assert-css: ("#main-content .docblock:not(.item-decl)", {"margin-left": "24px"})
index 5cf8a5e136ef79cfd47dc094264838b4c6c2c075..b65c398405cf5e36c12c59047e09715b9f2a9dcb 100644 (file)
@@ -3,15 +3,15 @@ goto: file://|DOC_PATH|/test_docs/index.html
 write: (".search-input", "test")
 wait-for: "#search > h1" // The search element is empty before the first search 
 assert-attribute: ("#search", {"class": "content"})
-assert-attribute: ("#main", {"class": "content hidden"})
+assert-attribute: ("#main-content", {"class": "content hidden"})
 press-key: "Escape"
 assert-attribute: ("#search", {"class": "content hidden"})
-assert-attribute: ("#main", {"class": "content"})
+assert-attribute: ("#main-content", {"class": "content"})
 
 // Check that focusing the search input brings back the search results
 focus: ".search-input"
 assert-attribute: ("#search", {"class": "content"})
-assert-attribute: ("#main", {"class": "content hidden"})
+assert-attribute: ("#main-content", {"class": "content hidden"})
 
 // Now let's check that when the help popup is displayed and we press Escape, it doesn't
 // hide the search results too.
@@ -20,7 +20,7 @@ assert-attribute: ("#help", {"class": ""})
 press-key: "Escape"
 assert-attribute: ("#help", {"class": "hidden"})
 assert-attribute: ("#search", {"class": "content"})
-assert-attribute: ("#main", {"class": "content hidden"})
+assert-attribute: ("#main-content", {"class": "content hidden"})
 
 // Check that Escape hides the search results when a search result is focused.
 focus: ".search-input"
@@ -31,4 +31,4 @@ assert: "#results a:focus"
 press-key: "Escape"
 assert-attribute: ("#help", {"class": "hidden"})
 assert-attribute: ("#search", {"class": "content hidden"})
-assert-attribute: ("#main", {"class": "content"})
+assert-attribute: ("#main-content", {"class": "content"})
index b268ec68d42e3b42e612a6e11d55784a690d0599..7c4496dc0cabc71e12f424e1e48101baa9907dac 100644 (file)
@@ -1,3 +1,3 @@
 // This test ensures that the impl blocks are open by default.
 goto: file://|DOC_PATH|/test_docs/struct.Foo.html
-assert-attribute: ("#main > details.implementors-toggle", {"open": ""})
+assert-attribute: ("#main-content > details.implementors-toggle", {"open": ""})
index 5d709f6588118690f94664cba083098d2262cbea..cadd7f6a3f3b54849c63eeea138a73d917c60aa9 100644 (file)
@@ -11,7 +11,7 @@ goto: file://|DOC_PATH|/test_docs/index.html?search=struct%3AFoo
 wait-for: "#titles"
 assert-text-false: (".fqn .in-band", "Struct test_docs::Foo")
 // Ensure that the search results are displayed, not the "normal" content.
-assert-css: ("#main", {"display": "none"})
+assert-css: ("#main-content", {"display": "none"})
 
 // Now we can check that the feature is working as expected!
 goto: file://|DOC_PATH|/test_docs/index.html?search=struct%3AFoo&go_to_first=true
index 5a49807e180b27d06fe1b15b157fb8dd14cff7b9..f3682f59d2141c758d720f2d9f0e667d5e74bd25 100644 (file)
@@ -1,6 +1,6 @@
 goto: file://|DOC_PATH|/src/test_docs/lib.rs.html
 // Check that we can click on the line number.
-click: (40, 224) // This is the position of the span for line 4.
+click: ".line-numbers > span:nth-child(4)" // This is the span for line 4.
 // Unfortunately, "#4" isn't a valid query selector, so we have to go around that limitation
 // by instead getting the nth span.
 assert-attribute: (".line-numbers > span:nth-child(4)", {"class": "line-highlighted"})
index 136868f3175c9722e8083a92caec6a13ce888332..eaa4c554d5a0fa9b2d9d9b4572faa9a7014ff9ed 100644 (file)
@@ -1,10 +1,10 @@
 goto: file://|DOC_PATH|/test_docs/index.html
-assert-attribute: ("#main > details.top-doc", {"open": ""})
+assert-attribute: ("#main-content > details.top-doc", {"open": ""})
 click: "#toggle-all-docs"
 wait-for: 1000
 // This is now collapsed so there shouldn't be the "open" attribute on details.
-assert-attribute-false: ("#main > details.top-doc", {"open": ""})
+assert-attribute-false: ("#main-content > details.top-doc", {"open": ""})
 click: "#toggle-all-docs"
 wait-for: 1000
 // Not collapsed anymore so the "open" attribute should be back.
-assert-attribute: ("#main > details.top-doc", {"open": ""})
+assert-attribute: ("#main-content > details.top-doc", {"open": ""})
index 63ab867fb17720c3e8cf8119076732d2f597a33e..bb24d0ce792545ad8d1a9ffd3d01286782e958f2 100644 (file)
@@ -11,7 +11,7 @@ assert-property: (".item-decl pre", {"scrollWidth": "1324"})
 goto: file://|DOC_PATH|/lib2/too_long/type.ReallyLongTypeNameLongLongLong.html
 assert-property: ("body", {"scrollWidth": "1100"})
 // We now check that the section width hasn't grown because of it.
-assert-property: ("#main", {"scrollWidth": "840"})
+assert-property: ("#main-content", {"scrollWidth": "840"})
 // And now checking that it has scrollable content.
 assert-property: (".item-decl pre", {"scrollWidth": "1103"})
 
@@ -20,6 +20,6 @@ assert-property: (".item-decl pre", {"scrollWidth": "1103"})
 goto: file://|DOC_PATH|/lib2/too_long/constant.ReallyLongTypeNameLongLongLongConstBecauseWhyNotAConstRightGigaGigaSupraLong.html
 assert-property: ("body", {"scrollWidth": "1100"})
 // We now check that the section width hasn't grown because of it.
-assert-property: ("#main", {"scrollWidth": "840"})
+assert-property: ("#main-content", {"scrollWidth": "840"})
 // And now checking that it has scrollable content.
 assert-property: (".item-decl pre", {"scrollWidth": "950"})
index 8b2b7870c25f73b8f5e1cec66522b9b3466afefc..9465c8a35c886f83e6681b4eb70c963b68ee47f3 100644 (file)
@@ -2,7 +2,7 @@
 #![feature(target_feature, cfg_target_feature)]
 
 // @has doc_cfg/struct.Portable.html
-// @!has - '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' ''
+// @!has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' ''
 // @has - '//*[@id="method.unix_and_arm_only_function"]' 'fn unix_and_arm_only_function()'
 // @has - '//*[@class="stab portability"]' 'This is supported on Unix and ARM only.'
 // @has - '//*[@id="method.wasi_and_wasm32_only_function"]' 'fn wasi_and_wasm32_only_function()'
 pub struct Portable;
 
 // @has doc_cfg/unix_only/index.html \
-//  '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+//  '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
 //  'This is supported on Unix only.'
 // @matches - '//*[@class="item-left module-item"]//*[@class="stab portability"]' '\AARM\Z'
 // @count - '//*[@class="stab portability"]' 2
 #[doc(cfg(unix))]
 pub mod unix_only {
     // @has doc_cfg/unix_only/fn.unix_only_function.html \
-    //  '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+    //  '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
     //  'This is supported on Unix only.'
     // @count - '//*[@class="stab portability"]' 1
     pub fn unix_only_function() {
@@ -25,7 +25,7 @@ pub fn unix_only_function() {
     }
 
     // @has doc_cfg/unix_only/trait.ArmOnly.html \
-    //  '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+    //  '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
     //  'This is supported on Unix and ARM only.'
     // @count - '//*[@class="stab portability"]' 1
     #[doc(cfg(target_arch = "arm"))]
@@ -40,14 +40,14 @@ fn unix_and_arm_only_function() {}
 }
 
 // @has doc_cfg/wasi_only/index.html \
-//  '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+//  '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
 //  'This is supported on WASI only.'
 // @matches - '//*[@class="item-left module-item"]//*[@class="stab portability"]' '\AWebAssembly\Z'
 // @count - '//*[@class="stab portability"]' 2
 #[doc(cfg(target_os = "wasi"))]
 pub mod wasi_only {
     // @has doc_cfg/wasi_only/fn.wasi_only_function.html \
-    //  '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+    //  '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
     //  'This is supported on WASI only.'
     // @count - '//*[@class="stab portability"]' 1
     pub fn wasi_only_function() {
@@ -55,7 +55,7 @@ pub fn wasi_only_function() {
     }
 
     // @has doc_cfg/wasi_only/trait.Wasm32Only.html \
-    //  '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+    //  '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
     //  'This is supported on WASI and WebAssembly only.'
     // @count - '//*[@class="stab portability"]' 1
     #[doc(cfg(target_arch = "wasm32"))]
@@ -77,7 +77,7 @@ fn wasi_and_wasm32_only_function() {}
 // @matches - '//*[@class="item-left module-item"]//*[@class="stab portability"]' '\Aavx\Z'
 
 // @has doc_cfg/fn.uses_target_feature.html
-// @has - '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+// @has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
 //        'This is supported with target feature avx only.'
 #[target_feature(enable = "avx")]
 pub unsafe fn uses_target_feature() {
@@ -85,7 +85,7 @@ pub unsafe fn uses_target_feature() {
 }
 
 // @has doc_cfg/fn.uses_cfg_target_feature.html
-// @has - '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+// @has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
 //        'This is supported with target feature avx only.'
 #[doc(cfg(target_feature = "avx"))]
 pub fn uses_cfg_target_feature() {
@@ -94,7 +94,7 @@ pub fn uses_cfg_target_feature() {
 
 // multiple attributes should be allowed
 // @has doc_cfg/fn.multiple_attrs.html \
-//  '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+//  '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
 //  'This is supported on x and y and z only.'
 #[doc(cfg(x))]
 #[doc(cfg(y), cfg(z))]
index 7ace13fe3a6b56dce0f24d2bb9cd61ebad400992..b018dd6cda58ac8f34819b988e4f5933fbfc3f39 100644 (file)
@@ -2,7 +2,7 @@
 // @has - '//h3[@class="code-header in-band"]' 'impl Foo'
 // @has - '//h3[@class="code-header in-band"]' 'impl Bar for Foo'
 // @count - '//*[@id="trait-implementations-list"]//*[@class="impl has-srclink"]' 1
-// @count - '//*[@id="main"]/details/summary/*[@class="impl has-srclink"]' 1
+// @count - '//*[@id="main-content"]/details/summary/*[@class="impl has-srclink"]' 1
 // @has issue_33054/impls/bar/trait.Bar.html
 // @has - '//h3[@class="code-header in-band"]' 'impl Bar for Foo'
 // @count - '//*[@class="struct"]' 1
index 742813262589b47d1d28c4febef6ae53d2c9a5f2..44885d4301ff8d79a7ce9ba21cd50cf6639237e5 100644 (file)
@@ -8,23 +8,23 @@ pub trait Foo2 {}
 impl Foo for Bar {}
 impl Foo2 for Bar {}
 
-// @has foo/fn.foo.html '//section[@id="main"]//pre' "x: &'x impl Foo"
-// @has foo/fn.foo.html '//section[@id="main"]//pre' "-> &'x impl Foo"
+// @has foo/fn.foo.html '//section[@id="main-content"]//pre' "x: &'x impl Foo"
+// @has foo/fn.foo.html '//section[@id="main-content"]//pre' "-> &'x impl Foo"
 pub fn foo<'x>(x: &'x impl Foo) -> &'x impl Foo {
     x
 }
 
-// @has foo/fn.foo2.html '//section[@id="main"]//pre' "x: &'x impl Foo"
-// @has foo/fn.foo2.html '//section[@id="main"]//pre' '-> impl Foo2'
+// @has foo/fn.foo2.html '//section[@id="main-content"]//pre' "x: &'x impl Foo"
+// @has foo/fn.foo2.html '//section[@id="main-content"]//pre' '-> impl Foo2'
 pub fn foo2<'x>(_x: &'x impl Foo) -> impl Foo2 {
     Bar
 }
 
-// @has foo/fn.foo_foo.html '//section[@id="main"]//pre' '-> impl Foo + Foo2'
+// @has foo/fn.foo_foo.html '//section[@id="main-content"]//pre' '-> impl Foo + Foo2'
 pub fn foo_foo() -> impl Foo + Foo2 {
     Bar
 }
 
-// @has foo/fn.foo_foo_foo.html '//section[@id="main"]//pre' "x: &'x impl Foo + Foo2"
+// @has foo/fn.foo_foo_foo.html '//section[@id="main-content"]//pre' "x: &'x impl Foo + Foo2"
 pub fn foo_foo_foo<'x>(_x: &'x (impl Foo + Foo2)) {
 }
index 47ac953f266099077066d61ba68153292cccb605..c3340af33d5a72c11506bfbe9ffb822e747d7675 100644 (file)
@@ -9,8 +9,8 @@
 #![crate_type = "rlib"]
 
 // @has prim_methods_external_core/index.html
-// @has - '//*[@id="main"]//a[@href="../my_core/primitive.char.html"]' 'char'
-// @has - '//*[@id="main"]//a[@href="../my_core/primitive.char.html#method.len_utf8"]' 'char::len_utf8'
+// @has - '//*[@id="main-content"]//a[@href="../my_core/primitive.char.html"]' 'char'
+// @has - '//*[@id="main-content"]//a[@href="../my_core/primitive.char.html#method.len_utf8"]' 'char::len_utf8'
 
 //! A [`char`] and its [`char::len_utf8`].
 
index 266bfa2073182e6dade9ee49251492e839e4c63f..fd0b1b97c6ef2653db8e707bf4e67dab2ff0b0a5 100644 (file)
@@ -3,10 +3,9 @@
 #![no_core]
 #![crate_type = "rlib"]
 
-
 // @has prim_methods_local/index.html
-// @has - '//*[@id="main"]//a[@href="primitive.char.html"]' 'char'
-// @has - '//*[@id="main"]//a[@href="primitive.char.html#method.len_utf8"]' 'char::len_utf8'
+// @has - '//*[@id="main-content"]//a[@href="primitive.char.html"]' 'char'
+// @has - '//*[@id="main-content"]//a[@href="primitive.char.html#method.len_utf8"]' 'char::len_utf8'
 
 //! A [prim@`char`] and its [`char::len_utf8`].
 
index a9c5d7d4247cef5d3641e84c20aef9f50c5b40d6..a412a23fda835e65f3296a9938485f7935a92d23 100644 (file)
@@ -1,8 +1,7 @@
 #![deny(rustdoc::broken_intra_doc_links)]
 
-
 // @has prim_methods/index.html
-// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.char.html"]' 'char'
-// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.char.html#method.len_utf8"]' 'char::len_utf8'
+// @has - '//*[@id="main-content"]//a[@href="{{channel}}/std/primitive.char.html"]' 'char'
+// @has - '//*[@id="main-content"]//a[@href="{{channel}}/std/primitive.char.html#method.len_utf8"]' 'char::len_utf8'
 
 //! A [`char`] and its [`char::len_utf8`].
index be9b3e420b7ad2a8aa05d0625fed6d5b38a7ce96..e02be9cabd26842386307979b1d6c140f3aff4b7 100644 (file)
@@ -1,9 +1,8 @@
 #![deny(rustdoc::broken_intra_doc_links)]
 #![crate_name = "foo"]
 
-
 // @has foo/index.html
-// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.bool.html"]' 'true'
-// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.bool.html"]' 'false'
+// @has - '//*[@id="main-content"]//a[@href="{{channel}}/std/primitive.bool.html"]' 'true'
+// @has - '//*[@id="main-content"]//a[@href="{{channel}}/std/primitive.bool.html"]' 'false'
 
 //! A `bool` is either [`true`] or [`false`].
index 70aa10767b270ca6552678ac642d19438b5c7656..14a6f5041f2085a6d5ba9f3c8025626d87627d39 100644 (file)
@@ -2,19 +2,19 @@
 
 // @has issue_55364/subone/index.html
 // These foo/bar links in the module's documentation should refer inside `subone`
-// @has - '//section[@id="main"]/details[@open=""]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/details[@open=""]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
+// @has - '//section[@id="main-content"]/details[@open=""]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
+// @has - '//section[@id="main-content"]/details[@open=""]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
 pub mod subone {
     //! See either [foo] or [bar].
 
     // This should refer to subone's `bar`
     // @has issue_55364/subone/fn.foo.html
-    // @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
+    // @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
     /// See [bar]
     pub fn foo() {}
     // This should refer to subone's `foo`
     // @has issue_55364/subone/fn.bar.html
-    // @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
+    // @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
     /// See [foo]
     pub fn bar() {}
 }
@@ -23,27 +23,27 @@ pub fn bar() {}
 
 // @has issue_55364/subtwo/index.html
 // These foo/bar links in the module's documentation should not reference inside `subtwo`
-// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
-// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
+// @!has - '//section[@id="main-content"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
+// @!has - '//section[@id="main-content"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
 // Instead it should be referencing the top level functions
-// @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
+// @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
+// @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
 // Though there should be such links later
-// @has - '//section[@id="main"]/div[@class="item-table"]//div[@class="item-left module-item"]/a[@class="fn"][@href="fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/div[@class="item-table"]//div[@class="item-left module-item"]/a[@class="fn"][@href="fn.bar.html"]' 'bar'
+// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-left module-item"]/a[@class="fn"][@href="fn.foo.html"]' 'foo'
+// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-left module-item"]/a[@class="fn"][@href="fn.bar.html"]' 'bar'
 /// See either [foo] or [bar].
 pub mod subtwo {
 
     // Despite the module's docs referring to the top level foo/bar,
     // this should refer to subtwo's `bar`
     // @has issue_55364/subtwo/fn.foo.html
-    // @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
+    // @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
     /// See [bar]
     pub fn foo() {}
     // Despite the module's docs referring to the top level foo/bar,
     // this should refer to subtwo's `foo`
     // @has issue_55364/subtwo/fn.bar.html
-    // @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
+    // @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
     /// See [foo]
     pub fn bar() {}
 }
@@ -59,8 +59,8 @@ pub fn bar() {}
 
 // @has issue_55364/subthree/index.html
 // This module should also refer to the top level foo/bar
-// @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
+// @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
+// @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
 pub mod subthree {
     //! See either [foo][super::foo] or [bar][super::bar]
 }
@@ -68,8 +68,8 @@ pub mod subthree {
 // Next we go *deeper* - In order to ensure it's not just "this or parent"
 // we test `crate::` and a `super::super::...` chain
 // @has issue_55364/subfour/subfive/subsix/subseven/subeight/index.html
-// @has - '//section[@id="main"]/div[@class="item-table"]//div[@class="item-right docblock-short"]//a[@href="../../../../../subone/fn.foo.html"]' 'other foo'
-// @has - '//section[@id="main"]/div[@class="item-table"]//div[@class="item-right docblock-short"]//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar'
+// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-right docblock-short"]//a[@href="../../../../../subone/fn.foo.html"]' 'other foo'
+// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-right docblock-short"]//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar'
 pub mod subfour {
     pub mod subfive {
         pub mod subsix {
index 16f7cac5f51cc77774a6372c1e8ec22280bbbf0a..29ceda4f7c1d3886d2ecf8ddcf9f6c9d6f0f4449 100644 (file)
@@ -8,7 +8,7 @@
 // @has foo/index.html '//div[@class="sidebar-elems"]//li/a/@href' '#keywords'
 // @has foo/keyword.match.html '//a[@class="keyword"]' 'match'
 // @has foo/keyword.match.html '//span[@class="in-band"]' 'Keyword match'
-// @has foo/keyword.match.html '//section[@id="main"]//div[@class="docblock"]//p' 'this is a test!'
+// @has foo/keyword.match.html '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!'
 // @has foo/index.html '//a/@href' '../foo/index.html'
 // @!has foo/foo/index.html
 // @!has-dir foo/foo
@@ -16,7 +16,7 @@
 /// this is a test!
 mod foo{}
 
-// @has foo/keyword.foo.html '//section[@id="main"]//div[@class="docblock"]//p' 'hello'
+// @has foo/keyword.foo.html '//section[@id="main-content"]//div[@class="docblock"]//p' 'hello'
 #[doc(keyword = "foo")]
 /// hello
 mod bar {}
index 96eb11311d6b43ba6ad3bfd410a03b1e726d2d0d..5468796f669eb2d2eac37a4557bb2dfd31d4da12 100644 (file)
@@ -3,7 +3,7 @@
 pub struct Struc;
 
 // @has foo/struct.Struc.html
-// @has - '//*[@id="main"]/h2[@id="implementations"]' "Implementations"
+// @has - '//*[@id="main-content"]/h2[@id="implementations"]' "Implementations"
 impl Struc {
     pub const S: u64 = 0;
 }
index c9fccf5a77cde6d31986f42812bc12a05a8165bb..dec7fe3f6a5d9620cbb0da78e845cc3d974c7bba 100644 (file)
 // @has foo/index.html '//a[@class="traitalias"]' 'Foo'
 
 // @has foo/traitalias.CopyAlias.html
-// @has - '//section[@id="main"]/div[@class="docblock item-decl"]/pre' 'trait CopyAlias = Copy;'
+// @has - '//section[@id="main-content"]/div[@class="docblock item-decl"]/pre' 'trait CopyAlias = Copy;'
 pub trait CopyAlias = Copy;
 // @has foo/traitalias.Alias2.html
-// @has - '//section[@id="main"]/div[@class="docblock item-decl"]/pre' 'trait Alias2 = Copy + Debug;'
+// @has - '//section[@id="main-content"]/div[@class="docblock item-decl"]/pre' 'trait Alias2 = Copy + Debug;'
 pub trait Alias2 = Copy + Debug;
 // @has foo/traitalias.Foo.html
-// @has - '//section[@id="main"]/div[@class="docblock item-decl"]/pre' 'trait Foo<T> = Into<T> + Debug;'
+// @has - '//section[@id="main-content"]/div[@class="docblock item-decl"]/pre' 'trait Foo<T> = Into<T> + Debug;'
 pub trait Foo<T> = Into<T> + Debug;
 // @has foo/fn.bar.html '//a[@href="traitalias.Alias2.html"]' 'Alias2'
 pub fn bar<T>() where T: Alias2 {}
index 139c5b4391ab7261860d3eb2f6f84ef99fd187af..2e339fe82649d6ca67a422bf58cdb495bc959b0c 100644 (file)
@@ -4,11 +4,11 @@
 // @has - '//h2[@id="fields"]' 'Tuple Fields'
 // @has - '//h3[@class="sidebar-title"]/a[@href="#fields"]' 'Tuple Fields'
 // @has - '//*[@id="structfield.0"]' '0: u32'
-// @has - '//*[@id="main"]/div[@class="docblock"]' 'hello'
+// @has - '//*[@id="main-content"]/div[@class="docblock"]' 'hello'
 // @!has - '//*[@id="structfield.1"]'
 // @has - '//*[@id="structfield.2"]' '2: char'
 // @has - '//*[@id="structfield.3"]' '3: i8'
-// @has - '//*[@id="main"]/div[@class="docblock"]' 'not hello'
+// @has - '//*[@id="main-content"]/div[@class="docblock"]' 'not hello'
 pub struct Foo(
     /// hello
     pub u32,
index c2840d7386f9829d72f3788044002cfa0ef2e671..a8165ae6c32693f24034b9528af8f17f9eb06152 100644 (file)
@@ -3,5 +3,5 @@
 // edition:2018
 // pp-exact
 
-fn main() { let _a = (async  { }); }
+fn main() { let _a = (async { }); }
 //~^ WARNING unnecessary parentheses around assigned value
index 0aea56ddb702b0386cec7e45011e04f1c4ec90bd..e3ed0b53356d42f9dccaeaac9170df5bf3a827c9 100644 (file)
@@ -1,14 +1,14 @@
 warning: unnecessary parentheses around assigned value
   --> $DIR/issue-54752-async-block.rs:6:22
    |
-LL | fn main() { let _a = (async  { }); }
-   |                      ^          ^
+LL | fn main() { let _a = (async { }); }
+   |                      ^         ^
    |
    = note: `#[warn(unused_parens)]` on by default
 help: remove these parentheses
    |
-LL - fn main() { let _a = (async  { }); }
-LL + fn main() { let _a = async  { }; }
+LL - fn main() { let _a = (async { }); }
+LL + fn main() { let _a = async { }; }
    | 
 
 warning: 1 warning emitted
index 395d9e21b3848a1b953baf4ef1a5429f1a003242..6f980e606645149fadfca22d1dfee4dd4061544d 100644 (file)
@@ -1,3 +1,3 @@
-async fn f(mut x : u8) { }
-async fn g((mut x, y, mut z) : (u8, u8, u8)) { }
-async fn g(mut x : u8, (a, mut b, c) : (u8, u8, u8), y : u8) { }
+async fn f(mut x : u8) {}
+async fn g((mut x, y, mut z) : (u8, u8, u8)) {}
+async fn g(mut x : u8, (a, mut b, c) : (u8, u8, u8), y : u8) {}
index d466157f04e0cd3c1f955fb69e7e2b02ed0ed5db..2718b65108cdd24c9d4a940e5928ead26ceddf85 100644 (file)
@@ -8,7 +8,7 @@ LL | #![u=||{static d=||1;}]
    |      ^^^^^^^^^^^^^^^^^
 
 error: unexpected token: `{
-           impl std::ops::Neg for i8 { }
+           impl std::ops::Neg for i8 {}
        }`
   --> $DIR/issue-90873.rs:7:6
    |
diff --git a/src/test/ui/cfg/auxiliary/crate-attributes-using-cfg_attr.rs b/src/test/ui/cfg/auxiliary/crate-attributes-using-cfg_attr.rs
deleted file mode 100644 (file)
index 1e0f5d7..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-// no-prefer-dynamic
-// compile-flags: --cfg foo
-
-#![cfg_attr(foo, crate_type="lib")]
-
-pub fn foo() {}
diff --git a/src/test/ui/cfg/crate-attributes-using-cfg_attr.rs b/src/test/ui/cfg/crate-attributes-using-cfg_attr.rs
deleted file mode 100644 (file)
index 43b266b..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-// run-pass
-// aux-build:crate-attributes-using-cfg_attr.rs
-
-extern crate crate_attributes_using_cfg_attr;
-
-pub fn main() {}
diff --git a/src/test/ui/cfg/future-compat-crate-attributes-using-cfg_attr.rs b/src/test/ui/cfg/future-compat-crate-attributes-using-cfg_attr.rs
new file mode 100644 (file)
index 0000000..ef12b05
--- /dev/null
@@ -0,0 +1,12 @@
+// check-fail
+// compile-flags:--cfg foo
+
+#![deny(warnings)]
+#![cfg_attr(foo, crate_type="bin")]
+//~^ERROR `crate_type` within
+//~| WARN this was previously accepted
+#![cfg_attr(foo, crate_name="bar")]
+//~^ERROR `crate_name` within
+//~| WARN this was previously accepted
+
+fn main() {}
diff --git a/src/test/ui/cfg/future-compat-crate-attributes-using-cfg_attr.stderr b/src/test/ui/cfg/future-compat-crate-attributes-using-cfg_attr.stderr
new file mode 100644 (file)
index 0000000..5df2eac
--- /dev/null
@@ -0,0 +1,26 @@
+error: `crate_type` within an `#![cfg_attr] attribute is deprecated`
+  --> $DIR/future-compat-crate-attributes-using-cfg_attr.rs:5:18
+   |
+LL | #![cfg_attr(foo, crate_type="bin")]
+   |                  ^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/future-compat-crate-attributes-using-cfg_attr.rs:4:9
+   |
+LL | #![deny(warnings)]
+   |         ^^^^^^^^
+   = note: `#[deny(deprecated_cfg_attr_crate_type_name)]` implied by `#[deny(warnings)]`
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #91632 <https://github.com/rust-lang/rust/issues/91632>
+
+error: `crate_name` within an `#![cfg_attr] attribute is deprecated`
+  --> $DIR/future-compat-crate-attributes-using-cfg_attr.rs:8:18
+   |
+LL | #![cfg_attr(foo, crate_name="bar")]
+   |                  ^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #91632 <https://github.com/rust-lang/rust/issues/91632>
+
+error: aborting due to 2 previous errors
+
index b57975e26f290b3667b35a347ef9609dc4f48878..d6c64d58be5f39c9975650203301179d702f500f 100644 (file)
@@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `Foo<{_: u32}>`
 LL |     let foo = Foo::foo();
    |         ---   ^^^^^^^^ cannot infer the value of const parameter `N`
    |         |
-   |         consider giving `foo` the explicit type `Foo<{_: u32}>`, where the type parameter `N` is specified
+   |         consider giving `foo` the explicit type `Foo<N>`, where the const parameter `N` is specified
 
 error: aborting due to previous error
 
index 1bceb8cbb94ddcc3b8e8ac179d353e43000dcb0d..9f65b8f25baaf85781800339386494aea82e6cb8 100644 (file)
@@ -11,9 +11,9 @@ use ::std::prelude::rust_2015::*;
 #[macro_use]
 extern crate std;
 
-trait Foo<const KIND : bool = true> { }
+trait Foo<const KIND : bool = true> {}
 
-fn foo<const SIZE : usize = 5>() { }
+fn foo<const SIZE : usize = 5>() {}
 
 struct Range<const FROM : usize = 0, const LEN : usize = 0, const TO : usize =
              FROM>;
index 0dfd804be41b48a0c44c0cae1a396c1d0c9b249f..18010413b9394528e0ef615a33b48fa5cfb2b1e3 100644 (file)
@@ -4,7 +4,7 @@ error: overly complex generic constant
 LL | fn test<const N: usize>() -> [u8; N + (|| 42)()] {}
    |                                   ^^^^-------^^
    |                                       |
-   |                                       dereferencing is not supported in generic constants
+   |                                       borrowing is not supported in generic constants
    |
    = help: consider moving this anonymous constant into a `const` function
    = note: this operation may be supported in the future
diff --git a/src/test/ui/const-generics/issues/issue-72845.rs b/src/test/ui/const-generics/issues/issue-72845.rs
new file mode 100644 (file)
index 0000000..bea5dc8
--- /dev/null
@@ -0,0 +1,49 @@
+#![feature(generic_const_exprs)]
+#![feature(specialization)]
+#![allow(incomplete_features)]
+
+//--------------------------------------------------
+
+trait Depth {
+    const C: usize;
+}
+
+trait Type {
+    type AT: Depth;
+}
+
+//--------------------------------------------------
+
+enum Predicate<const B: bool> {}
+
+trait Satisfied {}
+
+impl Satisfied for Predicate<true> {}
+
+//--------------------------------------------------
+
+trait Spec1 {}
+
+impl<T: Type> Spec1 for T where Predicate<{T::AT::C > 0}>: Satisfied {}
+
+trait Spec2 {}
+
+//impl<T: Type > Spec2 for T where Predicate<{T::AT::C > 1}>: Satisfied {}
+impl<T: Type > Spec2 for T where Predicate<true>: Satisfied {}
+
+//--------------------------------------------------
+
+trait Foo {
+    fn Bar();
+}
+
+impl<T: Spec1> Foo for T {
+    default fn Bar() {}
+}
+
+impl<T: Spec2> Foo for T {
+//~^ ERROR conflicting implementations of trait
+    fn Bar() {}
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/issues/issue-72845.stderr b/src/test/ui/const-generics/issues/issue-72845.stderr
new file mode 100644 (file)
index 0000000..631c860
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0119]: conflicting implementations of trait `Foo`
+  --> $DIR/issue-72845.rs:44:1
+   |
+LL | impl<T: Spec1> Foo for T {
+   | ------------------------ first implementation here
+...
+LL | impl<T: Spec2> Foo for T {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/src/test/ui/const-generics/issues/issue-90455.rs b/src/test/ui/const-generics/issues/issue-90455.rs
new file mode 100644 (file)
index 0000000..a580410
--- /dev/null
@@ -0,0 +1,12 @@
+#![feature(generic_const_exprs, adt_const_params)]
+#![allow(incomplete_features)]
+
+struct FieldElement<const N: &'static str> {
+    n: [u64; num_limbs(N)],
+    //~^ ERROR unconstrained generic constant
+}
+const fn num_limbs(_: &str) -> usize {
+    0
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/issues/issue-90455.stderr b/src/test/ui/const-generics/issues/issue-90455.stderr
new file mode 100644 (file)
index 0000000..724d7f4
--- /dev/null
@@ -0,0 +1,10 @@
+error: unconstrained generic constant
+  --> $DIR/issue-90455.rs:5:8
+   |
+LL |     n: [u64; num_limbs(N)],
+   |        ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: try adding a `where` bound using this expression: `where [(); num_limbs(N)]:`
+
+error: aborting due to previous error
+
index e6fecef9fb3b23fb238d753556b2463a9854201d..45a3d901c987119540915c195daa098f28fe0c01 100644 (file)
@@ -20,7 +20,7 @@ error[E0080]: evaluation of constant value failed
 LL |     let x: &'static i32 = &X;
    |                            ^ referenced constant has errors
 query stack during panic:
-#0 [normalize_mir_const_after_erasing_regions] normalizing `main::promoted[1]`
+#0 [try_normalize_mir_const_after_erasing_regions] normalizing `main::promoted[1]`
 #1 [optimized_mir] optimizing MIR for `main`
 #2 [collect_and_partition_mono_items] collect_and_partition_mono_items
 end of query stack
diff --git a/src/test/ui/consts/issue-90870.fixed b/src/test/ui/consts/issue-90870.fixed
new file mode 100644 (file)
index 0000000..e767eff
--- /dev/null
@@ -0,0 +1,34 @@
+// Regression test for issue #90870.
+
+// run-rustfix
+
+#![allow(dead_code)]
+
+const fn f(a: &u8, b: &u8) -> bool {
+    *a == *b
+    //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+    //~| HELP: consider dereferencing here
+}
+
+const fn g(a: &&&&i64, b: &&&&i64) -> bool {
+    ****a == ****b
+    //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+    //~| HELP: consider dereferencing here
+}
+
+const fn h(mut a: &[u8], mut b: &[u8]) -> bool {
+    while let ([l, at @ ..], [r, bt @ ..]) = (a, b) {
+        if *l == *r {
+        //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+        //~| HELP: consider dereferencing here
+            a = at;
+            b = bt;
+        } else {
+            return false;
+        }
+    }
+
+    a.is_empty() && b.is_empty()
+}
+
+fn main() {}
diff --git a/src/test/ui/consts/issue-90870.rs b/src/test/ui/consts/issue-90870.rs
new file mode 100644 (file)
index 0000000..35b3c82
--- /dev/null
@@ -0,0 +1,34 @@
+// Regression test for issue #90870.
+
+// run-rustfix
+
+#![allow(dead_code)]
+
+const fn f(a: &u8, b: &u8) -> bool {
+    a == b
+    //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+    //~| HELP: consider dereferencing here
+}
+
+const fn g(a: &&&&i64, b: &&&&i64) -> bool {
+    a == b
+    //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+    //~| HELP: consider dereferencing here
+}
+
+const fn h(mut a: &[u8], mut b: &[u8]) -> bool {
+    while let ([l, at @ ..], [r, bt @ ..]) = (a, b) {
+        if l == r {
+        //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+        //~| HELP: consider dereferencing here
+            a = at;
+            b = bt;
+        } else {
+            return false;
+        }
+    }
+
+    a.is_empty() && b.is_empty()
+}
+
+fn main() {}
diff --git a/src/test/ui/consts/issue-90870.stderr b/src/test/ui/consts/issue-90870.stderr
new file mode 100644 (file)
index 0000000..0e33e6e
--- /dev/null
@@ -0,0 +1,36 @@
+error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+  --> $DIR/issue-90870.rs:8:5
+   |
+LL |     a == b
+   |     ^^^^^^
+   |
+help: consider dereferencing here
+   |
+LL |     *a == *b
+   |     +     +
+
+error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+  --> $DIR/issue-90870.rs:14:5
+   |
+LL |     a == b
+   |     ^^^^^^
+   |
+help: consider dereferencing here
+   |
+LL |     ****a == ****b
+   |     ++++     ++++
+
+error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+  --> $DIR/issue-90870.rs:21:12
+   |
+LL |         if l == r {
+   |            ^^^^^^
+   |
+help: consider dereferencing here
+   |
+LL |         if *l == *r {
+   |            +     +
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0015`.
index 585f556abc8b538d31e0b011427527082fd15500..096440225b99907c57b1f73d35043c2198a12e3e 100644 (file)
@@ -9,11 +9,11 @@ LL |     let x = f == g;
 help: you might have forgotten to call this function
    |
 LL |     let x = f() == g;
-   |             ~~~
+   |              ++
 help: you might have forgotten to call this function
    |
 LL |     let x = f == g();
-   |                  ~~~
+   |                   ++
 
 error[E0308]: mismatched types
   --> $DIR/fn-compare-mismatch.rs:4:18
index b6a6a1ec2a6ed573503041b771ac433d3380a323..31f56565c49a6ce8b3f79c2aed4f991dbf8590bf 100644 (file)
@@ -3,6 +3,6 @@
 
 fn foo() => impl Fn() => bool {
     //~^ ERROR return types are denoted using `->`
-    //~| ERROR expected one of `+`, `->`, `::`, `;`, `where`, or `{`, found `=>`
+    //~| ERROR expected one of `+`, `->`, `::`, `where`, or `{`, found `=>`
     unimplemented!()
 }
index d62cacd4bf531b3057e1ef04df875ad6ba46b41d..25ee8dd0c5dcdb556582459d220ed2c5be7c44b8 100644 (file)
@@ -4,11 +4,11 @@ error: return types are denoted using `->`
 LL | fn foo() => impl Fn() => bool {
    |          ^^ help: use `->` instead
 
-error: expected one of `+`, `->`, `::`, `;`, `where`, or `{`, found `=>`
+error: expected one of `+`, `->`, `::`, `where`, or `{`, found `=>`
   --> $DIR/fn-recover-return-sign2.rs:4:23
    |
 LL | fn foo() => impl Fn() => bool {
-   |                       ^^ expected one of `+`, `->`, `::`, `;`, `where`, or `{`
+   |                       ^^ expected one of `+`, `->`, `::`, `where`, or `{`
 
 error: aborting due to 2 previous errors
 
index ffb9f9eed41fb92a1f20c322c893dff491fbf900..51c21043db8888d616bf8ff6302f620cfdde49f8 100644 (file)
@@ -15,7 +15,7 @@ fn bar /* 0#0 */() {
     y /* 0#1 */ + x /* 0#0 */
 }
 
-fn y /* 0#0 */() { }
+fn y /* 0#0 */() {}
 
 /*
 Expansions:
diff --git a/src/test/ui/inference/erase-type-params-in-label.rs b/src/test/ui/inference/erase-type-params-in-label.rs
new file mode 100644 (file)
index 0000000..1fea2da
--- /dev/null
@@ -0,0 +1,27 @@
+fn main() {
+    let foo = foo(1, ""); //~ ERROR E0283
+}
+fn baz() {
+    let bar = bar(1, ""); //~ ERROR E0283
+}
+
+struct Bar<T, K, N: Default> {
+    t: T,
+    k: K,
+    n: N,
+}
+
+fn bar<T, K, Z: Default>(t: T, k: K) -> Bar<T, K, Z> {
+    Bar { t, k, n: Default::default() }
+}
+
+struct Foo<T, K, N: Default, M: Default> {
+    t: T,
+    k: K,
+    n: N,
+    m: M,
+}
+
+fn foo<T, K, W: Default, Z: Default>(t: T, k: K) -> Foo<T, K, W, Z> {
+    Foo { t, k, n: Default::default(), m: Default::default() }
+}
diff --git a/src/test/ui/inference/erase-type-params-in-label.stderr b/src/test/ui/inference/erase-type-params-in-label.stderr
new file mode 100644 (file)
index 0000000..d0b06cd
--- /dev/null
@@ -0,0 +1,41 @@
+error[E0283]: type annotations needed for `Foo<i32, &str, W, Z>`
+  --> $DIR/erase-type-params-in-label.rs:2:15
+   |
+LL |     let foo = foo(1, "");
+   |         ---   ^^^ cannot infer type for type parameter `W` declared on the function `foo`
+   |         |
+   |         consider giving `foo` the explicit type `Foo<_, _, W, Z>`, where the type parameter `W` is specified
+   |
+   = note: cannot satisfy `_: Default`
+note: required by a bound in `foo`
+  --> $DIR/erase-type-params-in-label.rs:25:17
+   |
+LL | fn foo<T, K, W: Default, Z: Default>(t: T, k: K) -> Foo<T, K, W, Z> {
+   |                 ^^^^^^^ required by this bound in `foo`
+help: consider specifying the type arguments in the function call
+   |
+LL |     let foo = foo::<T, K, W, Z>(1, "");
+   |                  ++++++++++++++
+
+error[E0283]: type annotations needed for `Bar<i32, &str, Z>`
+  --> $DIR/erase-type-params-in-label.rs:5:15
+   |
+LL |     let bar = bar(1, "");
+   |         ---   ^^^ cannot infer type for type parameter `Z` declared on the function `bar`
+   |         |
+   |         consider giving `bar` the explicit type `Bar<_, _, Z>`, where the type parameter `Z` is specified
+   |
+   = note: cannot satisfy `_: Default`
+note: required by a bound in `bar`
+  --> $DIR/erase-type-params-in-label.rs:14:17
+   |
+LL | fn bar<T, K, Z: Default>(t: T, k: K) -> Bar<T, K, Z> {
+   |                 ^^^^^^^ required by this bound in `bar`
+help: consider specifying the type arguments in the function call
+   |
+LL |     let bar = bar::<T, K, Z>(1, "");
+   |                  +++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0283`.
index 65f3336b9358a3f205d20c617cbd9859aaefb945..9ca8f35fd545a4abb34cbae54e3ee8b9a8765443 100644 (file)
@@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `[usize; _]`
 LL |     let _ = foo("foo"); //<- Do not suggest `foo::<N>("foo");`!
    |         -   ^^^ cannot infer the value of const parameter `N` declared on the function `foo`
    |         |
-   |         consider giving this pattern the explicit type `[usize; _]`, where the type parameter `N` is specified
+   |         consider giving this pattern the explicit type `[_; N]`, where the const parameter `N` is specified
 
 error: aborting due to previous error
 
index f739557e001f53adc9b8792ddf582e4e27eef160..47fd9fdb65472970bebb8eca51ef764796b9f287 100644 (file)
@@ -5,7 +5,11 @@ LL |     foo > 12;
    |     --- ^ -- {integer}
    |     |
    |     fn() -> i32 {foo}
-   |     help: you might have forgotten to call this function: `foo()`
+   |
+help: you might have forgotten to call this function
+   |
+LL |     foo() > 12;
+   |        ++
 
 error[E0308]: mismatched types
   --> $DIR/issue-59488.rs:14:11
@@ -23,7 +27,11 @@ LL |     bar > 13;
    |     --- ^ -- {integer}
    |     |
    |     fn(i64) -> i64 {bar}
-   |     help: you might have forgotten to call this function: `bar( /* arguments */ )`
+   |
+help: you might have forgotten to call this function
+   |
+LL |     bar( /* arguments */ ) > 13;
+   |        +++++++++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/issue-59488.rs:18:11
@@ -45,11 +53,11 @@ LL |     foo > foo;
 help: you might have forgotten to call this function
    |
 LL |     foo() > foo;
-   |     ~~~~~
+   |        ++
 help: you might have forgotten to call this function
    |
 LL |     foo > foo();
-   |           ~~~~~
+   |              ++
 
 error[E0369]: binary operation `>` cannot be applied to type `fn() -> i32 {foo}`
   --> $DIR/issue-59488.rs:25:9
index cd4cc969200a6f5f6995b90001ae4bf3701fec48..4c11f3544948e694becba10e8d13aa8c0c0699f1 100644 (file)
@@ -6,9 +6,12 @@ LL |     assert_eq!(a, 0);
    |     |
    |     fn() -> i32 {a}
    |     {integer}
-   |     help: you might have forgotten to call this function: `*left_val()`
    |
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: you might have forgotten to call this function
+   |
+LL |                 if !(*left_val() == *right_val) {
+   |                               ++
 
 error[E0308]: mismatched types
   --> $DIR/issue-70724-add_type_neq_err_label-unwrap.rs:6:5
index ce8c118dab2426de9ad8c22488234ad297bbe8e9..c5f477cc4500ee9de04640c8ccfe41668266b561 100644 (file)
@@ -1,4 +1,4 @@
-// compile-flags: -Zfuture-incompat-test -Zemit-future-incompat-report
+// compile-flags: -Zfuture-incompat-test
 // check-pass
 
 // The `-Zfuture-incompat-test flag causes any normal warning to be included
index 68a076c93be614b0071e64e011bbee894f410401..4f81ee8b7e6bb59422b2849ace99552762492020 100644 (file)
@@ -11,4 +11,4 @@ extern crate std;
 
 macro_rules! foo { () => { break 'x ; } }
 
-pub fn main() { loop { } }
+pub fn main() { loop {} }
index 0ccdf6e5f2e74782688b98a08aa22b040bb04454..84fffe44d6a55a4d6a5b88ba0f7da7ebd6a1f9ac 100644 (file)
@@ -16,7 +16,7 @@
         struct S;
     }
 
-    n!(a $nt_item b); //~ ERROR no rules expected the token `enum E { }`
+    n!(a $nt_item b); //~ ERROR no rules expected the token `enum E {}`
 }
 
 simple_nonterminal!(a, 'a, (x, y, z)); // OK
index 155a94251312c7951a7c5febb1d78ac7ff04cbd3..585f2355321f5fac9da3a9ad0933e5d365519e25 100644 (file)
@@ -1,4 +1,4 @@
-error: no rules expected the token `enum E { }`
+error: no rules expected the token `enum E {}`
   --> $DIR/nonterminal-matching.rs:19:10
    |
 LL |     macro n(a $nt_item b) {
index 480d9bc2bade0d3506eec9a9bb0d9f704dbdd31a..017521f570c8201065d941f582b405fefaf539e9 100644 (file)
@@ -2,7 +2,7 @@
 // to happen in #24780. For example, following should be an error:
 // expected one of ..., `>`, ... found `>`.
 
-fn foo() -> Vec<usize>> { //~ ERROR expected one of `!`, `+`, `::`, `;`, `where`, or `{`, found `>`
+fn foo() -> Vec<usize>> { //~ ERROR expected one of `!`, `+`, `::`, `where`, or `{`, found `>`
     Vec::new()
 }
 
index bdd089bb7a13934ee379928e7f7c646fd955e028..d9470191b25a46c2aa062ee09fbe08276cb83708 100644 (file)
@@ -1,8 +1,8 @@
-error: expected one of `!`, `+`, `::`, `;`, `where`, or `{`, found `>`
+error: expected one of `!`, `+`, `::`, `where`, or `{`, found `>`
   --> $DIR/issue-24780.rs:5:23
    |
 LL | fn foo() -> Vec<usize>> {
-   |                       ^ expected one of `!`, `+`, `::`, `;`, `where`, or `{`
+   |                       ^ expected one of `!`, `+`, `::`, `where`, or `{`
 
 error: aborting due to previous error
 
index 332a3014416b57569985af36858c5b3b54f185a0..ea80eb8714f966c242688a576702c1743ab93be8 100644 (file)
@@ -2,7 +2,7 @@ impl A {
     //~^ ERROR cannot find type `A` in this scope
     fn b(self>
     //~^ ERROR expected one of `)`, `,`, or `:`, found `>`
-    //~| ERROR expected one of `->`, `;`, `where`, or `{`, found `>`
+    //~| ERROR expected one of `->`, `where`, or `{`, found `>`
 }
 
 fn main() {}
index 2afb26d17583471dc52cc76350436f6e82b8be39..96151f3fe07fd8ae3012e6ded78b9df39d03ae07 100644 (file)
@@ -6,14 +6,14 @@ LL |     fn b(self>
    |         |
    |         unclosed delimiter
 
-error: expected one of `->`, `;`, `where`, or `{`, found `>`
+error: expected one of `->`, `where`, or `{`, found `>`
   --> $DIR/issue-58856-1.rs:3:14
    |
 LL | impl A {
    |        - while parsing this item list starting here
 LL |
 LL |     fn b(self>
-   |              ^ expected one of `->`, `;`, `where`, or `{`
+   |              ^ expected one of `->`, `where`, or `{`
 ...
 LL | }
    | - the item list ends here
index 98506568d82c233519b4d7f75b908925a1bce198..77f0896e9c155d54462a4c5e416b14eb99b1c304 100644 (file)
@@ -13,11 +13,11 @@ LL | fn f(t:for<>t?)
    |              expected one of `(`, `)`, `+`, `,`, `::`, or `<`
    |              help: missing `,`
 
-error: expected one of `->`, `;`, `where`, or `{`, found `<eof>`
+error: expected one of `->`, `where`, or `{`, found `<eof>`
   --> $DIR/issue-84148-1.rs:1:15
    |
 LL | fn f(t:for<>t?)
-   |               ^ expected one of `->`, `;`, `where`, or `{`
+   |               ^ expected one of `->`, `where`, or `{`
 
 error: aborting due to 3 previous errors
 
index 6f314da436070371a5f23f93b12aa709dbabe576..396208316df677e32a085508eba833fd14217089 100644 (file)
@@ -21,11 +21,11 @@ LL | fn f(t:for<>t?
    |              expected one of `(`, `)`, `+`, `,`, `::`, or `<`
    |              help: missing `,`
 
-error: expected one of `->`, `;`, `where`, or `{`, found `<eof>`
+error: expected one of `->`, `where`, or `{`, found `<eof>`
   --> $DIR/issue-84148-2.rs:4:16
    |
 LL | fn f(t:for<>t?
-   |                ^ expected one of `->`, `;`, `where`, or `{`
+   |                ^ expected one of `->`, `where`, or `{`
 
 error: aborting due to 4 previous errors
 
index da74c1877b16500239587a384792f577a029f83e..f70a87fb0e8fbaad3f9b7cff2eb1e3b1ca78bb4e 100644 (file)
@@ -2,8 +2,8 @@ struct Foo {}
 
 impl Foo {
     pub fn bar()
-    //~^ ERROR: expected `;`, found `}`
-    //~| ERROR: associated function in `impl` without body
+    //~^ ERROR: associated function in `impl` without body
 }
+//~^ERROR expected one of `->`, `where`, or `{`, found `}`
 
 fn main() {}
index 920a9f937dd6b3900e83f41d9b145c57d8f131ef..0a52d0687b22beb97b2c1164ebed2c521b0910cf 100644 (file)
@@ -1,11 +1,13 @@
-error: expected `;`, found `}`
-  --> $DIR/issue-87635.rs:4:17
+error: expected one of `->`, `where`, or `{`, found `}`
+  --> $DIR/issue-87635.rs:6:1
    |
 LL |     pub fn bar()
-   |                 ^ help: add `;` here
-...
+   |            ---  - expected one of `->`, `where`, or `{`
+   |            |
+   |            while parsing this `fn`
+LL |
 LL | }
-   | - unexpected token
+   | ^ unexpected token
 
 error: associated function in `impl` without body
   --> $DIR/issue-87635.rs:4:5
index 22e1c2f97e76936c4a95f046afc3d45b0dd5cb87..3fe0d0f4273decd5b3a6137c08cd8571dfecff96 100644 (file)
@@ -22,11 +22,11 @@ error: expected one of `:` or `|`, found `)`
 LL | fn main((ؼ
    |           ^ expected one of `:` or `|`
 
-error: expected one of `->`, `;`, `where`, or `{`, found `<eof>`
+error: expected one of `->`, `where`, or `{`, found `<eof>`
   --> $DIR/missing_right_paren.rs:3:11
    |
 LL | fn main((ؼ
-   |           ^ expected one of `->`, `;`, `where`, or `{`
+   |           ^ expected one of `->`, `where`, or `{`
 
 error: aborting due to 4 previous errors
 
index eae169b162f4d6a9f3329316c16086901256c3f4..091862de30f7f032462c0e2d1f45c42080ceb0b5 100644 (file)
@@ -14,7 +14,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
         span: $DIR/allowed-attr-stmt-expr.rs:49:20: 49:21 (#0),
     },
 ]
-PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Foo { }
+PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Foo {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Punct {
         ch: '#',
@@ -140,7 +140,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
         span: $DIR/allowed-attr-stmt-expr.rs:61:28: 61:29 (#0),
     },
 ]
-PRINT-ATTR INPUT (DISPLAY): second_make_stmt! (#[allow(dead_code)] struct Bar { }) ;
+PRINT-ATTR INPUT (DISPLAY): second_make_stmt! (#[allow(dead_code)] struct Bar {}) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "second_make_stmt",
@@ -201,7 +201,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
         span: $DIR/allowed-attr-stmt-expr.rs:64:57: 64:58 (#0),
     },
 ]
-PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] #[allow(dead_code)] struct Bar { }
+PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] #[allow(dead_code)] struct Bar {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Punct {
         ch: '#',
@@ -257,7 +257,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
         span: $DIR/allowed-attr-stmt-expr.rs:64:54: 64:56 (#0),
     },
 ]
-PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Other { }
+PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Other {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Punct {
         ch: '#',
index fbefa3923ee22c1e2dcf88667b5d9b9844f4bae8..fc69a13ddb9b417f7dae9d356f1394d5c6e0e648 100644 (file)
@@ -1,4 +1,4 @@
-PRINT-ATTR INPUT (DISPLAY): fn foo < T : MyTrait < MyStruct < { true } >> > () { }
+PRINT-ATTR INPUT (DISPLAY): fn foo < T : MyTrait < MyStruct < { true } >> > () {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "fn",
index edb9fbab342e21120623b83001530368056075f3..f9b2305c7359bb07bbcc0a85a2923527f2e0d7ee 100644 (file)
@@ -1,4 +1,4 @@
-PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Foo { }
+PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Foo {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Punct {
         ch: '#',
@@ -124,7 +124,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
         span: $DIR/attr-stmt-expr.rs:53:28: 53:29 (#0),
     },
 ]
-PRINT-ATTR INPUT (DISPLAY): second_make_stmt! (#[allow(dead_code)] struct Bar { }) ;
+PRINT-ATTR INPUT (DISPLAY): second_make_stmt! (#[allow(dead_code)] struct Bar {}) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "second_make_stmt",
@@ -185,7 +185,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
         span: $DIR/attr-stmt-expr.rs:56:57: 56:58 (#0),
     },
 ]
-PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] #[allow(dead_code)] struct Bar { }
+PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] #[allow(dead_code)] struct Bar {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Punct {
         ch: '#',
@@ -241,7 +241,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
         span: $DIR/attr-stmt-expr.rs:56:54: 56:56 (#0),
     },
 ]
-PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Other { }
+PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Other {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Punct {
         ch: '#',
index c5b84b0367c8fbdf1c5c501a2811a574e1a54988..1b17d60476a8445765dcbc0ec725e68f41a83444 100644 (file)
@@ -83,7 +83,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
         span: $DIR/attribute-after-derive.rs:16:24: 19:2 (#0),
     },
 ]
-PRINT-DERIVE INPUT (DISPLAY): struct AttributeDerive { }
+PRINT-DERIVE INPUT (DISPLAY): struct AttributeDerive {}
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
@@ -99,7 +99,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
         span: $DIR/attribute-after-derive.rs:16:24: 19:2 (#0),
     },
 ]
-PRINT-DERIVE INPUT (DISPLAY): #[print_attr] struct DeriveAttribute { }
+PRINT-DERIVE INPUT (DISPLAY): #[print_attr] struct DeriveAttribute {}
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Punct {
         ch: '#',
index 8dd2a5ac7866f29392ad5ef2abd28a26c71ef177..5f76a4484e1a425ffde371c0947dba4345b5b9e3 100644 (file)
@@ -15,7 +15,7 @@ pub fn attr_with_args(args: TokenStream, input: TokenStream) -> TokenStream {
 
     let input = input.to_string();
 
-    assert_eq!(input, "fn foo() { }");
+    assert_eq!(input, "fn foo() {}");
 
     r#"
         fn foo() -> &'static str { "Hello, world!" }
index d89aaac59f6bd85d34b4d21d5d5fe0a8a08b2212..3787b8eeccc40b5151d895783db34c97fd1a77a3 100644 (file)
@@ -10,6 +10,6 @@
 #[proc_macro_attribute]
 pub fn foo(attr: TokenStream, item: TokenStream) -> TokenStream {
     drop(attr);
-    assert_eq!(item.to_string(), "fn foo() { }");
+    assert_eq!(item.to_string(), "fn foo() {}");
     "fn foo(&self);".parse().unwrap()
 }
index 08ead5aaeee8489f02212c7d105593180cdc4d08..debbad57a86994f644431d00ced7a18e8cdaa068 100644 (file)
@@ -3,7 +3,7 @@ PRINT-ATTR INPUT (DISPLAY): impl Foo <
  {
      #! [rustc_dummy(cursed_inner)] #! [allow(unused)] struct Inner
      { field : [u8 ; { #! [rustc_dummy(another_cursed_inner)] 1 }] } 0
- }] > { #! [rustc_dummy(evaluated_attr)] fn bar() { } }
+ }] > { #! [rustc_dummy(evaluated_attr)] fn bar() {} }
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "impl",
index 3ac1adf92c24f7dffe9447512f8cccdd161bce1f..dffbbf1494b9ae9d5ed9c1a04209e1f3369e0d74 100644 (file)
@@ -1,5 +1,5 @@
-Derive First: #[derive(Second)] #[derive(Third, Fourth)] #[derive(Fifth)] pub struct Foo { }
-Derive Second: #[derive(Third, Fourth)] #[derive(Fifth)] pub struct Foo { }
-Derive Third: #[derive(Fifth)] pub struct Foo { }
-Derive Fourth: #[derive(Fifth)] pub struct Foo { }
-Derive Fifth: pub struct Foo { }
+Derive First: #[derive(Second)] #[derive(Third, Fourth)] #[derive(Fifth)] pub struct Foo {}
+Derive Second: #[derive(Third, Fourth)] #[derive(Fifth)] pub struct Foo {}
+Derive Third: #[derive(Fifth)] pub struct Foo {}
+Derive Fourth: #[derive(Fifth)] pub struct Foo {}
+Derive Fifth: pub struct Foo {}
index e37a483cb87bc0335194cbc6968e6671d558360a..686d53e8876608a322b87badfbd6f4cd5b0b580f 100644 (file)
@@ -202,7 +202,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
         span: #8 bytes(430..483),
     },
 ]
-PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { { } } ; 0 }, }
+PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { {} } ; 0 }, }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "enum",
index 9b7865be6220ea075a6ceb7d80bced193c59a5aa..eaa8882d6a6cb6d60365cba92c941b67791ce09c 100644 (file)
@@ -269,7 +269,7 @@ PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
         span: $DIR/inner-attrs.rs:20:30: 20:36 (#0),
     },
 ]
-PRINT-ATTR INPUT (DISPLAY): fn foo() { }
+PRINT-ATTR INPUT (DISPLAY): fn foo() {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "fn",
@@ -552,7 +552,7 @@ PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
         span: $DIR/inner-attrs.rs:27:30: 27:40 (#0),
     },
 ]
-PRINT-ATTR INPUT (DISPLAY): mod inline_mod { }
+PRINT-ATTR INPUT (DISPLAY): mod inline_mod {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "mod",
@@ -933,7 +933,7 @@ PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
         span: $DIR/inner-attrs.rs:82:42: 82:47 (#0),
     },
 ]
-PRINT-ATTR INPUT (DISPLAY): fn weird_extern() { }
+PRINT-ATTR INPUT (DISPLAY): fn weird_extern() {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "fn",
index 866608e4d8e1d0a1aa5be7855fed1b6ec311c14c..44baa37577cfa25facbf015750f4abe8520638ae 100644 (file)
@@ -53,7 +53,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
         span: #4 bytes(432..433),
     },
 ]
-PRINT-DERIVE INPUT (DISPLAY): struct A { }
+PRINT-DERIVE INPUT (DISPLAY): struct A {}
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
index ea03653c52f833f6e785daf8107093f34c477e32..0fda6654ff37051f9a4c3c18d72256ee430fb547 100644 (file)
@@ -1,4 +1,4 @@
-PRINT-ATTR INPUT (DISPLAY): fn main() { & | _ : u8 | { } ; mul_2! (1 + 1) ; }
+PRINT-ATTR INPUT (DISPLAY): fn main() { & | _ : u8 | {} ; mul_2! (1 + 1) ; }
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "fn",
index 81b2b219c436815f7f3299b6bfd18a5d3c8a1df8..fdd178a52292f5d27df6d410295c6b3e54e5b5f4 100644 (file)
@@ -8,8 +8,8 @@ struct Foo < #[cfg(FALSE)] A, B >
          #[cfg(FALSE)] struct Bar ; #[cfg(not(FALSE))] struct Inner ;
          #[cfg(FALSE)] let a = 25 ; match true
          {
-             #[cfg(FALSE)] true => { },
-             #[cfg_attr(not(FALSE), allow(warnings))] false => { }, _ => { }
+             #[cfg(FALSE)] true => {},
+             #[cfg_attr(not(FALSE), allow(warnings))] false => {}, _ => {}
          } ; #[print_helper(should_be_removed)] fn removed_fn()
          { #! [cfg(FALSE)] } #[print_helper(c)] #[cfg(not(FALSE))] fn
          kept_fn() { #! [cfg(not(FALSE))] let my_val = true ; } enum TupleEnum
@@ -1278,7 +1278,7 @@ PRINT-DERIVE INPUT (DISPLAY): #[print_helper(a)] #[allow(dead_code)] #[print_hel
     [u8 ;
      {
          #[cfg(not(FALSE))] struct Inner ; match true
-         { #[allow(warnings)] false => { }, _ => { } } ; #[print_helper(c)]
+         { #[allow(warnings)] false => {}, _ => {} } ; #[print_helper(c)]
          #[cfg(not(FALSE))] fn kept_fn()
          { #! [cfg(not(FALSE))] let my_val = true ; } enum TupleEnum
          { Foo(#[cfg(not(FALSE))] i32, u8) } struct
index 6f880a120217002560b131b202a6b9068f006997..3c001e9954b5e440eed0175e791d2761e01cc2ad 100644 (file)
@@ -1,4 +1,4 @@
-PRINT-ATTR INPUT (DISPLAY): #[doc = r" A doc comment"] struct Foo { }
+PRINT-ATTR INPUT (DISPLAY): #[doc = r" A doc comment"] struct Foo {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Punct {
         ch: '#',
@@ -40,7 +40,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
         span: $DIR/issue-81007-item-attrs.rs:22:16: 22:18 (#0),
     },
 ]
-PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] #[doc = r" Another comment comment"] struct Bar { }
+PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] #[doc = r" Another comment comment"] struct Bar {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Punct {
         ch: '#',
index 8292617fc1675687a360a71b3ee0fff04a7ba3f1..68f30c23a8d284c57cea13612137d995dbcdb154 100644 (file)
@@ -11,7 +11,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
         span: $DIR/auxiliary/nested-macro-rules.rs:9:30: 9:35 (#6),
     },
 ]
-PRINT-ATTR INPUT (DISPLAY): struct FirstAttrStruct { }
+PRINT-ATTR INPUT (DISPLAY): struct FirstAttrStruct {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
@@ -46,7 +46,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
         span: $DIR/auxiliary/nested-macro-rules.rs:9:30: 9:35 (#15),
     },
 ]
-PRINT-ATTR INPUT (DISPLAY): struct SecondAttrStruct { }
+PRINT-ATTR INPUT (DISPLAY): struct SecondAttrStruct {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
index 709b2a2169e08d8f9033ba28e706f17f06756acc..c08e5308138c966deb834cceb151ad98e058099f 100644 (file)
@@ -64,7 +64,7 @@ macro inner /* 0#4 */ { () => { print_bang! { struct S; } } }
 struct S /* 0#5 */;
 // OK, not a duplicate definition of `S`
 
-fn main /* 0#0 */() { }
+fn main /* 0#0 */() {}
 
 /*
 Expansions:
index d60f400af2bb9c06839d35949056310cdf262a81..b90057cd6d5201819b7a03a231e5427ad18b385a 100644 (file)
@@ -1,4 +1,4 @@
-PRINT-ATTR INPUT (DISPLAY): fn foo < T > () where T : Copy + { }
+PRINT-ATTR INPUT (DISPLAY): fn foo < T > () where T : Copy + {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "fn",
index dc35df1159f883dc8a8feca4a3616b716c71df42..9bf5622173458b22660df683ba00d4fdf65ecd7d 100644 (file)
@@ -445,7 +445,7 @@ PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
         span: $DIR/weird-braces.rs:20:30: 20:42 (#0),
     },
 ]
-PRINT-ATTR INPUT (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > { }
+PRINT-ATTR INPUT (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > {}
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "impl",
index dad3641f0f5af87b125456ded6023cceaf40434e..aaf3993538acf1c7fecd52ab8d6ba76c76a0e79e 100644 (file)
@@ -13,7 +13,6 @@ extern crate std;
 // [pretty]compile-flags: -Zunpretty=everybody_loops
 // [pretty]check-pass
 #[repr("C")]
-struct A {
-}
+struct A {}
 
-fn main() { loop { } }
+fn main() { loop {} }
index aeee43b01cc021d62666b1e60c94b746b04f587d..6052ea95d0f85fe37666e2688ad1082b5f0ecb44 100644 (file)
@@ -7,4 +7,4 @@ extern crate std;
 // build-pass (FIXME(62277): could be check-pass?)
 // compile-flags: -Z unpretty=expanded
 
-fn main() { if let 0 = 1 { } }
+fn main() { if let 0 = 1 {} }
index 2a172c8458d71462493c1cb4a7b26d03c3e231b2..8800d3e66f9e42909fba8c00c93f17eafbb6d944 100644 (file)
@@ -18,14 +18,14 @@ pub fn $attr_name(attr: TokenStream, input: TokenStream) -> TokenStream {
 }
 
 checker!(attr_extern, r#"extern "C" { fn ffi(#[a1] arg1 : i32, #[a2] ...) ; }"#);
-checker!(attr_extern_cvar, r#"unsafe extern "C" fn cvar(arg1 : i32, #[a1] mut args : ...) { }"#);
+checker!(attr_extern_cvar, r#"unsafe extern "C" fn cvar(arg1 : i32, #[a1] mut args : ...) {}"#);
 checker!(attr_alias, "type Alias = fn(#[a1] u8, #[a2] ...) ;");
 checker!(attr_free, "fn free(#[a1] arg1 : u8) { let lam = | #[a2] W(x), #[a3] y | () ; }");
-checker!(attr_inherent_1, "fn inherent1(#[a1] self, #[a2] arg1 : u8) { }");
-checker!(attr_inherent_2, "fn inherent2(#[a1] & self, #[a2] arg1 : u8) { }");
-checker!(attr_inherent_3, "fn inherent3 < 'a > (#[a1] & 'a mut self, #[a2] arg1 : u8) { }");
-checker!(attr_inherent_4, "fn inherent4 < 'a > (#[a1] self : Box < Self >, #[a2] arg1 : u8) { }");
-checker!(attr_inherent_issue_64682, "fn inherent5(#[a1] #[a2] arg1 : u8, #[a3] arg2 : u8) { }");
+checker!(attr_inherent_1, "fn inherent1(#[a1] self, #[a2] arg1 : u8) {}");
+checker!(attr_inherent_2, "fn inherent2(#[a1] & self, #[a2] arg1 : u8) {}");
+checker!(attr_inherent_3, "fn inherent3 < 'a > (#[a1] & 'a mut self, #[a2] arg1 : u8) {}");
+checker!(attr_inherent_4, "fn inherent4 < 'a > (#[a1] self : Box < Self >, #[a2] arg1 : u8) {}");
+checker!(attr_inherent_issue_64682, "fn inherent5(#[a1] #[a2] arg1 : u8, #[a3] arg2 : u8) {}");
 checker!(attr_trait_1, "fn trait1(#[a1] self, #[a2] arg1 : u8) ;");
 checker!(attr_trait_2, "fn trait2(#[a1] & self, #[a2] arg1 : u8) ;");
 checker!(attr_trait_3, "fn trait3 < 'a > (#[a1] & 'a mut self, #[a2] arg1 : u8) ;");
@@ -35,9 +35,9 @@ pub fn $attr_name(attr: TokenStream, input: TokenStream) -> TokenStream {
 checker!(rename_params, r#"impl Foo
 {
     fn hello(#[angery(true)] a : i32, #[a2] b : i32, #[what = "how"] c : u32)
-    { } fn
+    {} fn
     hello2(#[a1] #[a2] a : i32, #[what = "how"] b : i32, #[angery(true)] c :
-           u32) { } fn
+           u32) {} fn
     hello_self(#[a1] #[a2] & self, #[a1] #[a2] a : i32, #[what = "how"] b :
-               i32, #[angery(true)] c : u32) { }
+               i32, #[angery(true)] c : u32) {}
 }"#);
diff --git a/src/test/ui/suggestions/count2len.rs b/src/test/ui/suggestions/count2len.rs
new file mode 100644 (file)
index 0000000..f11a789
--- /dev/null
@@ -0,0 +1,8 @@
+fn main() {
+    let slice = [1,2,3,4];
+    let vec = vec![1,2,3,4];
+
+    slice.count(); //~ERROR: E0599
+    vec.count(); //~ERROR: E0599
+    vec.as_slice().count(); //~ERROR: E0599
+}
diff --git a/src/test/ui/suggestions/count2len.stderr b/src/test/ui/suggestions/count2len.stderr
new file mode 100644 (file)
index 0000000..6394a84
--- /dev/null
@@ -0,0 +1,36 @@
+error[E0599]: no method named `count` found for array `[{integer}; 4]` in the current scope
+  --> $DIR/count2len.rs:5:11
+   |
+LL |     slice.count();
+   |           ^^^^^
+   |           |
+   |           method cannot be called on `[{integer}; 4]` due to unsatisfied trait bounds
+   |           help: consider using `len` instead
+   |
+   = note: `count` is defined on `Iterator`, which `[{integer}; 4]` does not implement
+
+error[E0599]: no method named `count` found for struct `Vec<{integer}>` in the current scope
+  --> $DIR/count2len.rs:6:9
+   |
+LL |     vec.count();
+   |         ^^^^^
+   |         |
+   |         method cannot be called on `Vec<{integer}>` due to unsatisfied trait bounds
+   |         help: consider using `len` instead
+   |
+   = note: `count` is defined on `Iterator`, which `Vec<{integer}>` does not implement
+
+error[E0599]: no method named `count` found for reference `&[{integer}]` in the current scope
+  --> $DIR/count2len.rs:7:20
+   |
+LL |     vec.as_slice().count();
+   |                    ^^^^^
+   |                    |
+   |                    method cannot be called on `&[{integer}]` due to unsatisfied trait bounds
+   |                    help: consider using `len` instead
+   |
+   = note: `count` is defined on `Iterator`, which `&[{integer}]` does not implement
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/suggestions/suggest-semicolon-for-fn-in-extern-block.fixed b/src/test/ui/suggestions/suggest-semicolon-for-fn-in-extern-block.fixed
new file mode 100644 (file)
index 0000000..5c55566
--- /dev/null
@@ -0,0 +1,9 @@
+// run-rustfix
+
+#[allow(dead_code)]
+
+extern "C" {
+  fn foo(); //~ERROR expected `;`
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/suggest-semicolon-for-fn-in-extern-block.rs b/src/test/ui/suggestions/suggest-semicolon-for-fn-in-extern-block.rs
new file mode 100644 (file)
index 0000000..91971cb
--- /dev/null
@@ -0,0 +1,9 @@
+// run-rustfix
+
+#[allow(dead_code)]
+
+extern "C" {
+  fn foo() //~ERROR expected `;`
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/suggest-semicolon-for-fn-in-extern-block.stderr b/src/test/ui/suggestions/suggest-semicolon-for-fn-in-extern-block.stderr
new file mode 100644 (file)
index 0000000..c5df72c
--- /dev/null
@@ -0,0 +1,10 @@
+error: expected `;`, found `}`
+  --> $DIR/suggest-semicolon-for-fn-in-extern-block.rs:6:11
+   |
+LL |   fn foo()
+   |           ^ help: add `;` here
+LL | }
+   | - unexpected token
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/traits/issue-85360-eval-obligation-ice.rs b/src/test/ui/traits/issue-85360-eval-obligation-ice.rs
new file mode 100644 (file)
index 0000000..1913168
--- /dev/null
@@ -0,0 +1,143 @@
+// compile-flags: --edition=2021
+
+#![feature(rustc_attrs)]
+
+use core::any::Any;
+use core::marker::PhantomData;
+
+fn main() {
+    test::<MaskedStorage<GenericComp<Pos>>>(make());
+    //~^ ERROR evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOk)
+    //~| ERROR evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOk)
+
+    test::<MaskedStorage<GenericComp2<Pos>>>(make());
+    //~^ ERROR evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp2<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOkModuloRegions)
+    //~| ERROR evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp2<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOkModuloRegions)
+}
+
+#[rustc_evaluate_where_clauses]
+fn test<T: Sized>(_: T) {}
+
+fn make<T>() -> T {
+    todo!()
+}
+
+struct DerefWrap<T>(T);
+
+impl<T> core::ops::Deref for DerefWrap<T> {
+    type Target = T;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+struct Storage<T, D> {
+    phantom: PhantomData<(T, D)>,
+}
+
+type ReadStorage<T> = Storage<T, DerefWrap<MaskedStorage<T>>>;
+
+pub trait Component {
+    type Storage;
+}
+
+struct VecStorage;
+
+struct Pos;
+
+impl Component for Pos {
+    type Storage = VecStorage;
+}
+
+struct GenericComp<T> {
+    _t: T,
+}
+
+impl<T: 'static> Component for GenericComp<T> {
+    type Storage = VecStorage;
+}
+
+struct GenericComp2<T> {
+    _t: T,
+}
+
+impl<T: 'static> Component for GenericComp2<T> where for<'a> &'a bool: 'a {
+    type Storage = VecStorage;
+}
+
+struct ReadData {
+    pos_interpdata: ReadStorage<GenericComp<Pos>>,
+}
+
+trait System {
+    type SystemData;
+
+    fn run(data: Self::SystemData, any: Box<dyn Any>);
+}
+
+struct Sys;
+
+impl System for Sys {
+    type SystemData = (ReadData, ReadStorage<Pos>);
+
+    fn run((data, pos): Self::SystemData, any: Box<dyn Any>) {
+        <ReadStorage<GenericComp<Pos>> as SystemData>::setup(any);
+
+        ParJoin::par_join((&pos, &data.pos_interpdata));
+    }
+}
+
+trait ParJoin {
+    fn par_join(self)
+    where
+        Self: Sized,
+    {
+    }
+}
+
+impl<'a, T, D> ParJoin for &'a Storage<T, D>
+where
+    T: Component,
+    D: core::ops::Deref<Target = MaskedStorage<T>>,
+    T::Storage: Sync,
+{
+}
+
+impl<A, B> ParJoin for (A, B)
+where
+    A: ParJoin,
+    B: ParJoin,
+{
+}
+
+pub trait SystemData {
+    fn setup(any: Box<dyn Any>);
+}
+
+impl<T: 'static> SystemData for ReadStorage<T>
+where
+    T: Component,
+{
+    fn setup(any: Box<dyn Any>) {
+        let storage: &MaskedStorage<T> = any.downcast_ref().unwrap();
+
+        <dyn Any as CastFrom<MaskedStorage<T>>>::cast(&storage);
+    }
+}
+
+pub struct MaskedStorage<T: Component> {
+    _inner: T::Storage,
+}
+
+pub unsafe trait CastFrom<T> {
+    fn cast(t: &T) -> &Self;
+}
+
+unsafe impl<T> CastFrom<T> for dyn Any
+where
+    T: Any + 'static,
+{
+    fn cast(t: &T) -> &Self {
+        t
+    }
+}
diff --git a/src/test/ui/traits/issue-85360-eval-obligation-ice.stderr b/src/test/ui/traits/issue-85360-eval-obligation-ice.stderr
new file mode 100644 (file)
index 0000000..ebf977d
--- /dev/null
@@ -0,0 +1,38 @@
+error: evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOk)
+  --> $DIR/issue-85360-eval-obligation-ice.rs:9:5
+   |
+LL |     test::<MaskedStorage<GenericComp<Pos>>>(make());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn test<T: Sized>(_: T) {}
+   |         - predicate
+
+error: evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOk)
+  --> $DIR/issue-85360-eval-obligation-ice.rs:9:5
+   |
+LL |     test::<MaskedStorage<GenericComp<Pos>>>(make());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn test<T: Sized>(_: T) {}
+   |            ----- predicate
+
+error: evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp2<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOkModuloRegions)
+  --> $DIR/issue-85360-eval-obligation-ice.rs:13:5
+   |
+LL |     test::<MaskedStorage<GenericComp2<Pos>>>(make());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn test<T: Sized>(_: T) {}
+   |         - predicate
+
+error: evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp2<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOkModuloRegions)
+  --> $DIR/issue-85360-eval-obligation-ice.rs:13:5
+   |
+LL |     test::<MaskedStorage<GenericComp2<Pos>>>(make());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn test<T: Sized>(_: T) {}
+   |            ----- predicate
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/typeck/issue-91267.rs b/src/test/ui/typeck/issue-91267.rs
new file mode 100644 (file)
index 0000000..f5a37e9
--- /dev/null
@@ -0,0 +1,6 @@
+fn main() {
+    0: u8<e<5>=e>
+    //~^ ERROR: cannot find type `e` in this scope [E0412]
+    //~| ERROR: associated type bindings are not allowed here [E0229]
+    //~| ERROR: mismatched types [E0308]
+}
diff --git a/src/test/ui/typeck/issue-91267.stderr b/src/test/ui/typeck/issue-91267.stderr
new file mode 100644 (file)
index 0000000..aac00b9
--- /dev/null
@@ -0,0 +1,27 @@
+error[E0412]: cannot find type `e` in this scope
+  --> $DIR/issue-91267.rs:2:16
+   |
+LL |     0: u8<e<5>=e>
+   |                ^
+   |                |
+   |                not found in this scope
+   |                help: maybe you meant to write an assignment here: `let e`
+
+error[E0229]: associated type bindings are not allowed here
+  --> $DIR/issue-91267.rs:2:11
+   |
+LL |     0: u8<e<5>=e>
+   |           ^^^^^^ associated type not allowed here
+
+error[E0308]: mismatched types
+  --> $DIR/issue-91267.rs:2:5
+   |
+LL | fn main() {
+   |           - expected `()` because of default return type
+LL |     0: u8<e<5>=e>
+   |     ^^^^^^^^^^^^^ expected `()`, found `u8`
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0229, E0308, E0412.
+For more information about an error, try `rustc --explain E0229`.
diff --git a/src/test/ui/typeck/issue-91334.rs b/src/test/ui/typeck/issue-91334.rs
new file mode 100644 (file)
index 0000000..bf9a5a6
--- /dev/null
@@ -0,0 +1,10 @@
+// Regression test for the ICE described in issue #91334.
+
+// error-pattern: this file contains an unclosed delimiter
+// error-pattern: expected one of
+// error-pattern: mismatched closing delimiter
+// error-pattern: mismatched types
+
+#![feature(generators)]
+
+fn f(){||yield(((){),
diff --git a/src/test/ui/typeck/issue-91334.stderr b/src/test/ui/typeck/issue-91334.stderr
new file mode 100644 (file)
index 0000000..358cc77
--- /dev/null
@@ -0,0 +1,50 @@
+error: this file contains an unclosed delimiter
+  --> $DIR/issue-91334.rs:10:23
+   |
+LL | fn f(){||yield(((){),
+   |       -       -       ^
+   |       |       |
+   |       |       unclosed delimiter
+   |       unclosed delimiter
+
+error: this file contains an unclosed delimiter
+  --> $DIR/issue-91334.rs:10:23
+   |
+LL | fn f(){||yield(((){),
+   |       -       -       ^
+   |       |       |
+   |       |       unclosed delimiter
+   |       unclosed delimiter
+
+error: expected one of `)`, `,`, `.`, `?`, or an operator, found `{`
+  --> $DIR/issue-91334.rs:10:19
+   |
+LL | fn f(){||yield(((){),
+   |                   ^
+   |                   |
+   |                   expected one of `)`, `,`, `.`, `?`, or an operator
+   |                   help: missing `,`
+
+error: mismatched closing delimiter: `)`
+  --> $DIR/issue-91334.rs:10:19
+   |
+LL | fn f(){||yield(((){),
+   |                -  ^^ mismatched closing delimiter
+   |                |  |
+   |                |  unclosed delimiter
+   |                closing delimiter possibly meant for this
+
+error[E0308]: mismatched types
+  --> $DIR/issue-91334.rs:10:8
+   |
+LL | fn f(){||yield(((){),
+   |       -^^^^^^^^^^^^^^^ expected `()`, found generator
+   |       |
+   |       help: try adding a return type: `-> [generator@$DIR/issue-91334.rs:10:8: 10:23]`
+   |
+   = note: expected unit type `()`
+              found generator `[generator@$DIR/issue-91334.rs:10:8: 10:23]`
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/typeck/issue-91450-inner-ty-error.rs b/src/test/ui/typeck/issue-91450-inner-ty-error.rs
new file mode 100644 (file)
index 0000000..0b942d6
--- /dev/null
@@ -0,0 +1,7 @@
+// Regression test for #91450.
+// This test ensures that the compiler does not suggest `Foo<[type error]>` in diagnostic messages.
+
+fn foo() -> Option<_> {} //~ ERROR: [E0308]
+//~^ ERROR: the type placeholder `_` is not allowed
+
+fn main() {}
diff --git a/src/test/ui/typeck/issue-91450-inner-ty-error.stderr b/src/test/ui/typeck/issue-91450-inner-ty-error.stderr
new file mode 100644 (file)
index 0000000..314fe56
--- /dev/null
@@ -0,0 +1,21 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-91450-inner-ty-error.rs:4:13
+   |
+LL | fn foo() -> Option<_> {}
+   |    ---      ^^^^^^^^^ expected enum `Option`, found `()`
+   |    |
+   |    implicitly returns `()` as its body has no tail or `return` expression
+   |
+   = note:   expected enum `Option<_>`
+           found unit type `()`
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures for return types
+  --> $DIR/issue-91450-inner-ty-error.rs:4:20
+   |
+LL | fn foo() -> Option<_> {}
+   |                    ^ not allowed in type signatures
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0121, E0308.
+For more information about an error, try `rustc --explain E0121`.
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md
deleted file mode 100644 (file)
index 866303a..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
----
-name: Blank Issue
-about: Create a blank issue.
----
-
-
-<!--
-Additional labels can be added to this issue by including the following command
-(without the space after the @ symbol):
-
-@ rustbot label +<label>
-
-Common labels for this issue type are:
-* C-an-interesting-project
-* C-enhancement
-* C-question
-* C-tracking-issue
--->
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.yml
new file mode 100644 (file)
index 0000000..d610e8c
--- /dev/null
@@ -0,0 +1,44 @@
+name: Blank Issue
+description: Create a blank issue.
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for filing an issue!
+  - type: textarea
+    id: problem
+    attributes:
+      label: Description
+      description: >
+        Please provide a discription of the issue, along with any information
+        you feel relevant to replicate it.
+    validations:
+      required: true
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+        binary: rustc
+        commit-hash: f455e46eae1a227d735091091144601b467e1565
+        commit-date: 2020-06-20
+        host: x86_64-unknown-linux-gnu
+        release: 1.46.0-nightly
+        LLVM version: 10.0
+      render: text
+  - type: textarea
+    id: labels
+    attributes:
+      label: Additional Labels
+      description: >
+        Additional labels can be added to this issue by including the following
+        command
+      placeholder: |
+        @rustbot label +<label>
+
+        Common labels for this issue type are:
+        * C-an-interesting-project
+        * C-enhancement
+        * C-question
+        * C-tracking-issue
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644 (file)
index 119a498..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
----
-name: Bug Report
-about: Create a bug report for Clippy
-labels: C-bug
----
-<!--
-Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
-along with any information you feel relevant to replicating the bug.
--->
-
-I tried this code:
-
-```rust
-<code>
-```
-
-I expected to see this happen: *explanation*
-
-Instead, this happened: *explanation*
-
-### Meta
-
-**Rust version (`rustc -Vv`):**
-
-```
-rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-binary: rustc
-commit-hash: f455e46eae1a227d735091091144601b467e1565
-commit-date: 2020-06-20
-host: x86_64-unknown-linux-gnu
-release: 1.46.0-nightly
-LLVM version: 10.0
-```
-
-<!--
-Additional labels can be added to this issue by including the following command
-(without the space after the @ symbol):
-
-@ rustbot label +<label>
-
-Common labels for this issue type are:
-* `I-suggestion-causes-error`
--->
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644 (file)
index 0000000..68877ef
--- /dev/null
@@ -0,0 +1,57 @@
+name: Bug Report
+description: Create a bug report for Clippy
+labels: ["C-bug"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for filing a bug report! 🐛
+  - type: textarea
+    id: problem
+    attributes:
+      label: Summary
+      description: >
+        Please provide a short summary of the bug, along with any information
+        you feel relevant to replicate the bug.
+    validations:
+      required: true
+  - type: textarea
+    id: reproducer
+    attributes:
+      label: Reproducer
+      description: Please provide the code and steps to repoduce the bug
+      value: |
+        I tried this code:
+
+        ```rust
+        <code>
+        ```
+
+        I expected to see this happen:
+
+        Instead, this happened:
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+        binary: rustc
+        commit-hash: f455e46eae1a227d735091091144601b467e1565
+        commit-date: 2020-06-20
+        host: x86_64-unknown-linux-gnu
+        release: 1.46.0-nightly
+        LLVM version: 10.0
+      render: text
+  - type: textarea
+    id: labels
+    attributes:
+      label: Additional Labels
+      description: >
+        Additional labels can be added to this issue by including the following
+        command
+      placeholder: |
+        @rustbot label +<label>
+
+        Common labels for this issue type are:
+        * `I-suggestion-causes-error`
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md
deleted file mode 100644 (file)
index d9ea2db..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
----
-name: Bug Report (False Negative)
-about: Create a bug report about missing warnings from a lint
-labels: C-bug, I-false-negative
----
-<!--
-Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
-along with any information you feel relevant to replicating the bug.
--->
-Lint name:
-
-
-I tried this code:
-
-```rust
-<code>
-```
-
-I expected to see this happen: *explanation*
-
-Instead, this happened: *explanation*
-
-### Meta
-
-**Rust version (`rustc -Vv`):**
-
-```
-rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-binary: rustc
-commit-hash: f455e46eae1a227d735091091144601b467e1565
-commit-date: 2020-06-20
-host: x86_64-unknown-linux-gnu
-release: 1.46.0-nightly
-LLVM version: 10.0
-```
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.yml
new file mode 100644 (file)
index 0000000..9357ccc
--- /dev/null
@@ -0,0 +1,50 @@
+name: Bug Report (False Negative)
+description: Create a bug report about missing warnings from a lint
+labels: ["C-bug", "I-false-negative"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for filing a bug report! 🐛
+  - type: textarea
+    id: problem
+    attributes:
+      label: Summary
+      description: >
+        Please provide a short summary of the bug, along with any information
+        you feel relevant to replicate the bug.
+    validations:
+      required: true
+  - type: input
+    id: lint-name
+    attributes:
+      label: Lint Name
+      description: Please provide the lint name.
+  - type: textarea
+    id: reproducer
+    attributes:
+      label: Reproducer
+      description: Please provide the code and steps to repoduce the bug
+      value: |
+        I tried this code:
+
+        ```rust
+        <code>
+        ```
+
+        I expected to see this happen:
+
+        Instead, this happened:
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+        binary: rustc
+        commit-hash: f455e46eae1a227d735091091144601b467e1565
+        commit-date: 2020-06-20
+        host: x86_64-unknown-linux-gnu
+        release: 1.46.0-nightly
+        LLVM version: 10.0
+      render: text
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md
deleted file mode 100644 (file)
index 82158e0..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
----
-name: Bug Report (False Positive)
-about: Create a bug report about a wrongly emitted lint warning
-labels: C-bug, I-false-positive
----
-<!--
-Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
-along with any information you feel relevant to replicating the bug.
--->
-Lint name:
-
-
-I tried this code:
-
-```rust
-<code>
-```
-
-I expected to see this happen: *explanation*
-
-Instead, this happened: *explanation*
-
-### Meta
-
-**Rust version (`rustc -Vv`):**
-```
-rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-binary: rustc
-commit-hash: f455e46eae1a227d735091091144601b467e1565
-commit-date: 2020-06-20
-host: x86_64-unknown-linux-gnu
-release: 1.46.0-nightly
-LLVM version: 10.0
-```
-
-<!--
-Additional labels can be added to this issue by including the following command
-(without the space after the @ symbol):
-
-@ rustbot label +<label>
-
-Common labels for this issue type are:
-* I-suggestion-causes-error
--->
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.yml
new file mode 100644 (file)
index 0000000..b7dd400
--- /dev/null
@@ -0,0 +1,68 @@
+name: Bug Report (False Positive)
+description: Create a bug report about a wrongly emitted lint warning
+labels: ["C-bug", "I-false-positive"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for filing a bug report! 🐛
+  - type: textarea
+    id: problem
+    attributes:
+      label: Summary
+      description: >
+        Please provide a short summary of the bug, along with any information
+        you feel relevant to replicate the bug.
+    validations:
+      required: true
+  - type: input
+    id: lint-name
+    attributes:
+      label: Lint Name
+      description: Please provide the lint name.
+  - type: textarea
+    id: reproducer
+    attributes:
+      label: Reproducer
+      description: >
+        Please provide the code and steps to repoduce the bug together with the
+        output from Clippy.
+      value: |
+        I tried this code:
+
+        ```rust
+        <code>
+        ```
+
+        I saw this happen:
+
+        ```
+        <output>
+        ```
+
+        I expected to see this happen:
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+        binary: rustc
+        commit-hash: f455e46eae1a227d735091091144601b467e1565
+        commit-date: 2020-06-20
+        host: x86_64-unknown-linux-gnu
+        release: 1.46.0-nightly
+        LLVM version: 10.0
+      render: text
+  - type: textarea
+    id: labels
+    attributes:
+      label: Additional Labels
+      description: >
+        Additional labels can be added to this issue by including the following
+        command
+      placeholder: |
+        @rustbot label +<label>
+
+        Common labels for this issue type are:
+        * `I-suggestion-causes-error`
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md
deleted file mode 100644 (file)
index 6c1bed6..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
----
-name: Internal Compiler Error
-about: Create a report for an internal compiler error in Clippy.
-labels: C-bug, I-ICE
----
-<!--
-Thank you for finding an Internal Compiler Error! 🧊  If possible, try to provide
-a minimal verifiable example. You can read "Rust Bug Minimization Patterns" for
-how to create smaller examples.
-
-http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
-
--->
-
-### Code
-
-```rust
-<code>
-```
-
-### Meta
-
-**Rust version (`rustc -Vv`):**
-```
-rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-binary: rustc
-commit-hash: f455e46eae1a227d735091091144601b467e1565
-commit-date: 2020-06-20
-host: x86_64-unknown-linux-gnu
-release: 1.46.0-nightly
-LLVM version: 10.0
-```
-
-### Error output
-
-```
-<output>
-```
-
-<!--
-Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in your
-environment. E.g. `RUST_BACKTRACE=1 cargo clippy`.
--->
-<details><summary>Backtrace</summary>
-  <p>
-  
-  ```
-  <backtrace>
-  ```
-  
-  </p>
-</details>
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.yml
new file mode 100644 (file)
index 0000000..2a5b8b3
--- /dev/null
@@ -0,0 +1,48 @@
+name: Internal Compiler Error
+description: Create a report for an internal compiler error (ICE) in Clippy.
+labels: ["C-bug", "I-ICE"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for finding an Internal Compiler Error! 🧊
+  - type: textarea
+    id: problem
+    attributes:
+      label: Summary
+      description: |
+        If possible, try to provide a minimal verifiable example. You can read ["Rust Bug Minimization Patterns"][mve] for how to create smaller examples. Otherwise, provide the crate where the ICE occured.
+
+        [mve]: http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
+    validations:
+      required: true
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+        binary: rustc
+        commit-hash: f455e46eae1a227d735091091144601b467e1565
+        commit-date: 2020-06-20
+        host: x86_64-unknown-linux-gnu
+        release: 1.46.0-nightly
+        LLVM version: 10.0
+      render: text
+  - type: textarea
+    id: error
+    attributes:
+      label: Error output
+      description: >
+        Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in
+        your environment. E.g. `RUST_BACKTRACE=1 cargo clippy`.
+      value: |
+        <details><summary>Backtrace</summary>
+          <p>
+
+          ```
+          <backtrace>
+          ```
+
+          </p>
+        </details>
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md
deleted file mode 100644 (file)
index 2216bb9..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
----
-name: New lint suggestion
-about: Suggest a new Clippy lint.
-labels: A-lint
----
-
-### What it does
-
-*What does this lint do?*
-
-### Categories (optional)
-
-- Kind: *See <https://github.com/rust-lang/rust-clippy/blob/master/README.md#clippy> for list of lint kinds*
-
-*What is the advantage of the recommended code over the original code*
-
-For example:
-- Remove bounds check inserted by ...
-- Remove the need to duplicate/store ...
-- Remove typo ...
-
-### Drawbacks
-
-None.
-
-### Example
-
-```rust
-<code>
-```
-
-Could be written as:
-
-```rust
-<code>
-```
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml
new file mode 100644 (file)
index 0000000..0b43d8d
--- /dev/null
@@ -0,0 +1,71 @@
+name: New lint suggestion
+description: Suggest a new Clippy lint.
+labels: ["A-lint"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for your lint idea!
+  - type: textarea
+    id: what
+    attributes:
+      label: What it does
+      description: What does this lint do?
+    validations:
+      required: true
+  - type: input
+    id: lint-name
+    attributes:
+      label: Lint Name
+      description: Please provide the lint name.
+  - type: dropdown
+    id: category
+    attributes:
+      label: Category
+      description: >
+        What category should this lint go into? If you're unsure you can select
+        multiple categories. You can find a category description in the
+        `README`.
+      multiple: true
+      options:
+        - correctness
+        - suspicious
+        - style
+        - complexity
+        - perf
+        - pedantic
+        - restriction
+        - cargo
+  - type: textarea
+    id: advantage
+    attributes:
+      label: Advantage
+      description: >
+        What is the advantage of the recommended code over the original code?
+      placeholder: |
+        - Remove bounds check inserted by ...
+        - Remove the need to duplicate/store ...
+        - Remove typo ...
+  - type: textarea
+    id: drawbacks
+    attributes:
+      label: Drawbacks
+      description: What might be possible drawbacks of such a lint?
+  - type: textarea
+    id: example
+    attributes:
+      label: Example
+      description: >
+        Include a short example showing when the lint should trigger together
+        with the improved code.
+      value: |
+        ```rust
+        <code>
+        ```
+
+        Could be written as:
+
+        ```rust
+        <code>
+        ```
+    validations:
+      required: true
index 9a5416153abdb62856d4ad34305f806294aa43f9..fe8bce00fa82e162ebcffca2944482dcefe1ffe9 100644 (file)
@@ -25,18 +25,6 @@ jobs:
     - name: Checkout
       uses: actions/checkout@v2.3.3
 
-    - name: remove toolchain file
-      run: rm rust-toolchain
-
-    - name: rust-toolchain
-      uses: actions-rs/toolchain@v1.0.6
-      with:
-        toolchain: nightly
-        target: x86_64-unknown-linux-gnu
-        profile: minimal
-        components: rustfmt
-        default: true
-
     # Run
     - name: Build
       run: cargo build --features deny-warnings
index 85a6a6be8b7f2618d9b515afc201e54f27781e13..157ea0c963afc5d1d8748ebe79ce250c2343914d 100644 (file)
@@ -70,7 +70,7 @@ Current beta, release 2021-12-02
   [#7560](https://github.com/rust-lang/rust-clippy/pull/7560)
 * [`unnecessary_unwrap`]: Now also checks for `expect`s
   [#7584](https://github.com/rust-lang/rust-clippy/pull/7584)
-* [`disallowed_method`]: Allow adding a reason that will be displayed with the
+* [`disallowed_methods`]: Allow adding a reason that will be displayed with the
   lint message
   [#7621](https://github.com/rust-lang/rust-clippy/pull/7621)
 * [`approx_constant`]: Now checks the MSRV for `LOG10_2` and `LOG2_10`
@@ -174,7 +174,7 @@ Current stable, released 2021-10-21
 
 * [`needless_continue`]: Now also lints in `loop { continue; }` case
   [#7477](https://github.com/rust-lang/rust-clippy/pull/7477)
-* [`disallowed_type`]: Now also primitive types can be disallowed
+* [`disallowed_types`]: Now also primitive types can be disallowed
   [#7488](https://github.com/rust-lang/rust-clippy/pull/7488)
 * [`manual_swap`]: Now also lints on xor swaps
   [#7506](https://github.com/rust-lang/rust-clippy/pull/7506)
@@ -248,7 +248,7 @@ Released 2021-09-09
   [#7403](https://github.com/rust-lang/rust-clippy/pull/7403)
 * [`disallowed_script_idents`]
   [#7400](https://github.com/rust-lang/rust-clippy/pull/7400)
-* [`disallowed_type`]
+* [`disallowed_types`]
   [#7315](https://github.com/rust-lang/rust-clippy/pull/7315)
 * [`missing_enforced_import_renames`]
   [#7300](https://github.com/rust-lang/rust-clippy/pull/7300)
@@ -294,7 +294,7 @@ Released 2021-09-09
   [#7379](https://github.com/rust-lang/rust-clippy/pull/7379)
 * [`redundant_closure`]: Suggests `&mut` for `FnMut`
   [#7437](https://github.com/rust-lang/rust-clippy/pull/7437)
-* [`disallowed_method`], [`disallowed_type`]: The configuration values `disallowed-method` and `disallowed-type`
+* [`disallowed_methods`], [`disallowed_types`]: The configuration values `disallowed-method` and `disallowed-type`
   no longer require fully qualified paths
   [#7345](https://github.com/rust-lang/rust-clippy/pull/7345)
 * [`zst_offset`]: Fixed lint invocation after it was accidentally suppressed
@@ -703,7 +703,7 @@ Released 2021-05-06
 
 ### Enhancements
 
-* [`disallowed_method`]: Now supports functions in addition to methods
+* [`disallowed_methods`]: Now supports functions in addition to methods
   [#6674](https://github.com/rust-lang/rust-clippy/pull/6674)
 * [`upper_case_acronyms`]: Added a new configuration `upper-case-acronyms-aggressive` to
   trigger the lint if there is more than one uppercase character next to each other
@@ -1044,7 +1044,7 @@ Released 2020-12-31
 
 * [`field_reassign_with_default`] [#5911](https://github.com/rust-lang/rust-clippy/pull/5911)
 * [`await_holding_refcell_ref`] [#6029](https://github.com/rust-lang/rust-clippy/pull/6029)
-* [`disallowed_method`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
+* [`disallowed_methods`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
 * [`inline_asm_x86_att_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 * [`inline_asm_x86_intel_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 * [`from_iter_instead_of_collect`] [#6101](https://github.com/rust-lang/rust-clippy/pull/6101)
@@ -2821,9 +2821,9 @@ Released 2018-09-13
 [`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
 [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
 [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
-[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
+[`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods
 [`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
-[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
+[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
 [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
 [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
 [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
@@ -2904,6 +2904,7 @@ Released 2018-09-13
 [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
 [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
 [`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
+[`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice
 [`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
 [`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
 [`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
@@ -3032,12 +3033,14 @@ Released 2018-09-13
 [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
 [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
 [`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
+[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
 [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
 [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
 [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
 [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
 [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
 [`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
+[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
 [`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
 [`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
 [`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
@@ -3054,6 +3057,7 @@ Released 2018-09-13
 [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
 [`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
 [`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
+[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
 [`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
 [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
 [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
index 602877bb9d6831dfd824f6d0e8a4fdf143e96b59..8661a86775887285bc953c4ec7afbbb68a313830 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.58"
+version = "0.1.59"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
@@ -47,6 +47,7 @@ itertools = "0.10"
 quote = "1.0"
 serde = { version = "1.0", features = ["derive"] }
 syn = { version = "1.0", features = ["full"] }
+parking_lot = "0.11.2"
 
 [build-dependencies]
 rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
index affb283017c8ce50252ae6d9750e553591e21497..d350d9a001827e899b183fc19bcf5952eb43bb29 100644 (file)
@@ -12,6 +12,7 @@ opener = "0.5"
 regex = "1.5"
 shell-escape = "0.1"
 walkdir = "2.3"
+cargo_metadata = "0.14"
 
 [features]
 deny-warnings = []
index c81eb40d52f3551ffc698db36acf7767b5c3874e..9ceadee58ea5981777ff171d7a94ad7c9f09e41d 100644 (file)
@@ -1,6 +1,7 @@
 use crate::clippy_project_root;
+use itertools::Itertools;
 use shell_escape::escape;
-use std::ffi::OsStr;
+use std::ffi::{OsStr, OsString};
 use std::path::Path;
 use std::process::{self, Command};
 use std::{fs, io};
@@ -56,15 +57,22 @@ fn try_run(context: &FmtContext) -> Result<bool, CliError> {
         success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?;
         success &= cargo_fmt(context, &project_root.join("lintcheck"))?;
 
-        for entry in WalkDir::new(project_root.join("tests")) {
-            let entry = entry?;
-            let path = entry.path();
-
-            if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
-                continue;
-            }
-
-            success &= rustfmt(context, path)?;
+        let chunks = WalkDir::new(project_root.join("tests"))
+            .into_iter()
+            .filter_map(|entry| {
+                let entry = entry.expect("failed to find tests");
+                let path = entry.path();
+
+                if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
+                    None
+                } else {
+                    Some(entry.into_path().into_os_string())
+                }
+            })
+            .chunks(250);
+
+        for chunk in &chunks {
+            success &= rustfmt(context, chunk)?;
         }
 
         Ok(success)
@@ -149,7 +157,7 @@ fn exec(
 }
 
 fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
-    let mut args = vec!["+nightly", "fmt", "--all"];
+    let mut args = vec!["fmt", "--all"];
     if context.check {
         args.push("--");
         args.push("--check");
@@ -162,7 +170,7 @@ fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
 fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
     let program = "rustfmt";
     let dir = std::env::current_dir()?;
-    let args = &["+nightly", "--version"];
+    let args = &["--version"];
 
     if context.verbose {
         println!("{}", format_command(&program, &dir, args));
@@ -185,14 +193,14 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
     }
 }
 
-fn rustfmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
-    let mut args = vec!["+nightly".as_ref(), path.as_os_str()];
+fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Result<bool, CliError> {
+    let mut args = Vec::new();
     if context.check {
-        args.push("--check".as_ref());
+        args.push(OsString::from("--check"));
     }
+    args.extend(paths);
+
     let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?;
-    if !success {
-        eprintln!("rustfmt failed on {}", path.display());
-    }
+
     Ok(success)
 }
index 5538f62c8e786c0f2f16092fa5a754ace47c5988..59fde447547145f3093b5054112bd58dd6ec8b7e 100644 (file)
@@ -7,6 +7,7 @@
 
 pub mod bless;
 pub mod fmt;
+pub mod lint;
 pub mod new_lint;
 pub mod serve;
 pub mod setup;
diff --git a/src/tools/clippy/clippy_dev/src/lint.rs b/src/tools/clippy/clippy_dev/src/lint.rs
new file mode 100644 (file)
index 0000000..dfd16f7
--- /dev/null
@@ -0,0 +1,20 @@
+use std::process::{self, Command};
+
+pub fn run(filename: &str) {
+    let code = Command::new("cargo")
+        .args(["run", "--bin", "clippy-driver", "--"])
+        .args(["-L", "./target/debug"])
+        .args(["-Z", "no-codegen"])
+        .args(["--edition", "2021"])
+        .arg(filename)
+        .env("__CLIPPY_INTERNAL_TESTS", "true")
+        .status()
+        .expect("failed to run cargo")
+        .code();
+
+    if code.is_none() {
+        eprintln!("Killed by signal");
+    }
+
+    process::exit(code.unwrap_or(1));
+}
index b5c04efce3bc95bca885102df44ed2c612c6e8f1..30a241c8ba151e1cfa3b1385156f13ddd892eb06 100644 (file)
@@ -3,7 +3,7 @@
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
 use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
-use clippy_dev::{bless, fmt, new_lint, serve, setup, update_lints};
+use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints};
 fn main() {
     let matches = get_clap_config();
 
@@ -55,6 +55,10 @@ fn main() {
             let lint = matches.value_of("lint");
             serve::run(port, lint);
         },
+        ("lint", Some(matches)) => {
+            let filename = matches.value_of("filename").unwrap();
+            lint::run(filename);
+        },
         _ => {},
     }
 }
@@ -219,5 +223,14 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
                 )
                 .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
         )
+        .subcommand(
+            SubCommand::with_name("lint")
+                .about("Manually run clippy on a file")
+                .arg(
+                    Arg::with_name("filename")
+                        .required(true)
+                        .help("The path to a file to lint"),
+                ),
+        )
         .get_matches()
 }
index 43a478ee77db826975fcbda09202af1381a2981b..59658b42c79b14405773817052214034e13cedbc 100644 (file)
@@ -132,6 +132,18 @@ fn to_camel_case(name: &str) -> String {
         .collect()
 }
 
+fn get_stabilisation_version() -> String {
+    let mut command = cargo_metadata::MetadataCommand::new();
+    command.no_deps();
+    if let Ok(metadata) = command.exec() {
+        if let Some(pkg) = metadata.packages.iter().find(|pkg| pkg.name == "clippy") {
+            return format!("{}.{}.0", pkg.version.minor, pkg.version.patch);
+        }
+    }
+
+    String::from("<TODO set version(see doc/adding_lints.md)>")
+}
+
 fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
     let mut contents = format!(
         indoc! {"
@@ -178,6 +190,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
         },
     };
 
+    let version = get_stabilisation_version();
     let lint_name = lint.name;
     let category = lint.category;
     let name_camel = to_camel_case(lint.name);
@@ -212,7 +225,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
     });
 
     result.push_str(&format!(
-        indoc! {"
+        indoc! {r#"
             declare_clippy_lint! {{
                 /// ### What it does
                 ///
@@ -226,11 +239,13 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
                 /// ```rust
                 /// // example code which does not raise clippy warning
                 /// ```
+                #[clippy::version = "{version}"]
                 pub {name_upper},
                 {category},
-                \"default lint description\"
+                "default lint description"
             }}
-        "},
+        "#},
+        version = version,
         name_upper = name_upper,
         category = category,
     ));
index 23f58bc4915f919eed14c71f50684c77ff2b52c9..8dd073ef405a172960a8886488ec61b183c6d906 100644 (file)
@@ -18,6 +18,7 @@
         r#"(?x)
     declare_clippy_lint!\s*[\{(]
     (?:\s+///.*)*
+    (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
     \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
     (?P<cat>[a-z_]+)\s*,\s*
     "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
@@ -31,6 +32,7 @@
         r#"(?x)
     declare_deprecated_lint!\s*[{(]\s*
     (?:\s+///.*)*
+    (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
     \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
     "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
 "#,
@@ -495,6 +497,7 @@ fn test_parse_contents() {
     let result: Vec<Lint> = parse_contents(
         r#"
 declare_clippy_lint! {
+    #[clippy::version = "Hello Clippy!"]
     pub PTR_ARG,
     style,
     "really long \
@@ -502,6 +505,7 @@ fn test_parse_contents() {
 }
 
 declare_clippy_lint!{
+    #[clippy::version = "Test version"]
     pub DOC_MARKDOWN,
     pedantic,
     "single line"
@@ -509,6 +513,7 @@ fn test_parse_contents() {
 
 /// some doc comment
 declare_deprecated_lint! {
+    #[clippy::version = "I'm a version"]
     pub SHOULD_ASSERT_EQ,
     "`assert!()` will be more flexible with RFC 2011"
 }
index 281480b8d94914c2d1835a1fb7d2d1fb62620b18..0661c2803864c15545b050f15a2ddb9a3fd43634 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_lints"
-version = "0.1.58"
+version = "0.1.59"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
index 1483f3f9185aeb741d4f76b4b29ad18250c6263d..7665aa8380b3c396594f5af3a066bd9b72eef463 100644 (file)
@@ -36,6 +36,7 @@
     /// if vec.len() <= 0 {}
     /// if 100 > i32::MAX {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ABSURD_EXTREME_COMPARISONS,
     correctness,
     "a comparison with a maximum or minimum value that is always true or false"
index fb54ac1ec511981595402d73dfd3bba329b3e903..12435eefbc4eeaf89816d09a1493c8049f85c61b 100644 (file)
@@ -33,6 +33,7 @@
     /// let x = std::f32::consts::PI;
     /// let y = std::f64::consts::FRAC_1_PI;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub APPROX_CONSTANT,
     correctness,
     "the approximate of a known float constant (in `std::fXX::consts`)"
index 36fe7b7a8675441943a430129f70e00f90bc875c..e0c1d6ab6e12235577859c606f9e572dcbaf9f0f 100644 (file)
@@ -25,6 +25,7 @@
     /// # let a = 0;
     /// a + 1;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INTEGER_ARITHMETIC,
     restriction,
     "any integer arithmetic expression which could overflow or panic"
@@ -43,6 +44,7 @@
     /// # let a = 0.0;
     /// a + 1.0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FLOAT_ARITHMETIC,
     restriction,
     "any floating-point arithmetic statement"
index 0be460d67a75f3e8bbb248782978eed3ba1a79fb..53704da1046bcf236a5a511c0b83a5557b6ba4ab 100644 (file)
@@ -38,6 +38,7 @@
     /// f(a.try_into().expect("Unexpected u16 overflow in f"));
     /// ```
     ///
+    #[clippy::version = "1.41.0"]
     pub AS_CONVERSIONS,
     restriction,
     "using a potentially dangerous silent `as` conversion"
index 825832eb79dab8e15bc49ed42a73fac97338223e..0322698f029556d3e0fc341ddc3e4f2df3f9db42 100644 (file)
@@ -75,6 +75,7 @@ fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr
     /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
     /// # }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub INLINE_ASM_X86_INTEL_SYNTAX,
     restriction,
     "prefer AT&T x86 assembly syntax"
@@ -111,6 +112,7 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
     /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
     /// # }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub INLINE_ASM_X86_ATT_SYNTAX,
     restriction,
     "prefer Intel x86 assembly syntax"
index d834a1d317a0f1d7e9d3344fb764cc3a2ab866d4..521fc84ee9c375ae3d74416afd0b8a60bb1fc86c 100644 (file)
@@ -26,6 +26,7 @@
     /// const B: bool = false;
     /// assert!(B)
     /// ```
+    #[clippy::version = "1.34.0"]
     pub ASSERTIONS_ON_CONSTANTS,
     style,
     "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`"
index 2097a1feff9f362025189849c3f93ae48d8233f2..e16f4369da9fcdd488063d232f2f70890e86bef4 100644 (file)
@@ -34,6 +34,7 @@
     /// // Good
     /// a += b;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ASSIGN_OP_PATTERN,
     style,
     "assigning the result of an operation on a variable to that same variable"
@@ -60,6 +61,7 @@
     /// // ...
     /// a += a + b;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MISREFACTORED_ASSIGN_OP,
     suspicious,
     "having a variable on both sides of an assign op"
index 182736a5a205a6ab3178ef7de4e83af150df74e8..0619490e73c4361da3284406b0b8bdceff50a128 100644 (file)
@@ -34,6 +34,7 @@
     ///   };
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub ASYNC_YIELDS_ASYNC,
     correctness,
     "async blocks that return a type that can be awaited"
index 6f8b645dd70d1f7981633f304b2139dbfc4ac15b..1edb7c950e7b1d0bd141654178cf032d731b3a59 100644 (file)
@@ -1,8 +1,9 @@
 //! checks for attributes
 
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::match_panic_def_id;
+use clippy_utils::msrvs;
 use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
+use clippy_utils::{extract_msrv_attr, match_panic_def_id, meets_msrv};
 use if_chain::if_chain;
 use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
 use rustc_errors::Applicability;
@@ -12,7 +13,8 @@
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Span;
 use rustc_span::sym;
 use rustc_span::symbol::{Symbol, SymbolStr};
@@ -64,6 +66,7 @@
     /// #[inline(always)]
     /// fn not_quite_hot_code(..) { ... }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INLINE_ALWAYS,
     pedantic,
     "use of `#[inline(always)]`"
     /// #[macro_use]
     /// extern crate baz;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_ATTRIBUTE,
     correctness,
     "use of lint attributes on `extern crate` items"
     /// #[deprecated(since = "forever")]
     /// fn something_else() { /* ... */ }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DEPRECATED_SEMVER,
     correctness,
     "use of `#[deprecated(since = \"x\")]` where x is not semver"
     /// #[allow(dead_code)]
     /// fn this_is_fine_too() { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EMPTY_LINE_AFTER_OUTER_ATTR,
     nursery,
     "empty line after outer attribute"
     /// ```rust
     /// #![deny(clippy::as_conversions)]
     /// ```
+    #[clippy::version = "1.47.0"]
     pub BLANKET_CLIPPY_RESTRICTION_LINTS,
     suspicious,
     "enabling the complete restriction group"
     /// #[rustfmt::skip]
     /// fn main() { }
     /// ```
+    #[clippy::version = "1.32.0"]
     pub DEPRECATED_CFG_ATTR,
     complexity,
     "usage of `cfg_attr(rustfmt)` instead of tool attributes"
     /// fn conditional() { }
     /// ```
     /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
+    #[clippy::version = "1.45.0"]
     pub MISMATCHED_TARGET_OS,
     correctness,
     "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
@@ -497,7 +506,11 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
     }
 }
 
-declare_lint_pass!(EarlyAttributes => [
+pub struct EarlyAttributes {
+    pub msrv: Option<RustcVersion>,
+}
+
+impl_lint_pass!(EarlyAttributes => [
     DEPRECATED_CFG_ATTR,
     MISMATCHED_TARGET_OS,
     EMPTY_LINE_AFTER_OUTER_ATTR,
@@ -509,9 +522,11 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
     }
 
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
-        check_deprecated_cfg_attr(cx, attr);
+        check_deprecated_cfg_attr(cx, attr, self.msrv);
         check_mismatched_target_os(cx, attr);
     }
+
+    extract_msrv_attr!(EarlyContext);
 }
 
 fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
@@ -548,8 +563,9 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It
     }
 }
 
-fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
+fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option<RustcVersion>) {
     if_chain! {
+        if meets_msrv(msrv.as_ref(), &msrvs::TOOL_ATTRIBUTES);
         // check cfg_attr
         if attr.has_name(sym::cfg_attr);
         if let Some(items) = attr.meta_item_list();
index 28615b9217cd342df10851a1a308409f6c36a693..1cc3418d4748c98728116538fba64a80949abefc 100644 (file)
@@ -47,6 +47,7 @@
     ///   bar.await;
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub AWAIT_HOLDING_LOCK,
     pedantic,
     "Inside an async function, holding a MutexGuard while calling await"
@@ -88,6 +89,7 @@
     ///   bar.await;
     /// }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub AWAIT_HOLDING_REFCELL_REF,
     pedantic,
     "Inside an async function, holding a RefCell ref while calling await"
index 11346e7c96af98b4f074eca91afd0de745ce4cba..0977cf22b2c4e00343756b8ef381f66291ae24f5 100644 (file)
@@ -41,6 +41,7 @@
     /// # let x = 1;
     /// if (x & 1 == 2) { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BAD_BIT_MASK,
     correctness,
     "expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
@@ -73,6 +74,7 @@
     /// # let x = 1;
     /// if (x | 1 > 3) {  }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INEFFECTIVE_BIT_MASK,
     correctness,
     "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`"
@@ -95,6 +97,7 @@
     /// # let x = 1;
     /// if x & 0b1111 == 0 { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub VERBOSE_BIT_MASK,
     pedantic,
     "expressions where a bit mask is less readable than the corresponding method call"
index 916c78c982ae4e63863ada1ecd1d928cf74aab7e..1600fb25d89e24cec4b9935477b1d19f25a4987d 100644 (file)
@@ -17,6 +17,7 @@
     /// ```rust
     /// let foo = 3.14;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BLACKLISTED_NAME,
     style,
     "usage of a blacklisted/placeholder name"
index 47e5b0d583dabcee654d1613e6057a06459f5112..b59f49357df241c9f6a0cd33a9cebb26d142aeee 100644 (file)
@@ -41,6 +41,7 @@
     /// let res = { let x = somefunc(); x };
     /// if res { /* ... */ }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub BLOCKS_IN_IF_CONDITIONS,
     style,
     "useless or complex blocks that can be eliminated in conditions"
index cdc192a47e48abebb325f95437ce48a1ee00dee6..d0b8c52a36a92e9891c33dd1bee45e7ef6fab7b5 100644 (file)
@@ -23,6 +23,7 @@
     /// // Good
     /// assert!(!"a".is_empty());
     /// ```
+    #[clippy::version = "1.53.0"]
     pub BOOL_ASSERT_COMPARISON,
     style,
     "Using a boolean as comparison value in an assert_* macro when there is no need"
@@ -72,7 +73,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             if let Some(span) = is_direct_expn_of(expr.span, mac) {
                 if let Some(args) = higher::extract_assert_macro_args(expr) {
                     if let [a, b, ..] = args[..] {
-                        let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize;
+                        let nb_bool_args = usize::from(is_bool_lit(a)) + usize::from(is_bool_lit(b));
 
                         if nb_bool_args != 1 {
                             // If there are two boolean arguments, we definitely don't understand
index 8282800c81904fa93f2bd49a934d6deecd866b73..51835ee7488fb01fe3090b45633eb0039dbb6743 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-use clippy_utils::{eq_expr_value, get_trait_def_id, in_macro, paths};
+use clippy_utils::{eq_expr_value, get_trait_def_id, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -31,6 +31,7 @@
     /// if a && true  // should be: if a
     /// if !(a == b)  // should be: if a != b
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NONMINIMAL_BOOL,
     complexity,
     "boolean expressions that can be written more concisely"
@@ -52,6 +53,7 @@
     /// if a && b || a { ... }
     /// ```
     /// The `b` is unnecessary, the expression is equivalent to `if a`.
+    #[clippy::version = "pre 1.29.0"]
     pub LOGIC_BUG,
     correctness,
     "boolean expressions that contain terminals which can be eliminated"
@@ -453,22 +455,20 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
     type Map = Map<'tcx>;
 
     fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
-        if in_macro(e.span) {
-            return;
-        }
-        match &e.kind {
-            ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => {
-                self.bool_expr(e);
-            },
-            ExprKind::Unary(UnOp::Not, inner) => {
-                if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() {
+        if !e.span.from_expansion() {
+            match &e.kind {
+                ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => {
                     self.bool_expr(e);
-                } else {
-                    walk_expr(self, e);
-                }
-            },
-            _ => walk_expr(self, e),
+                },
+                ExprKind::Unary(UnOp::Not, inner) => {
+                    if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() {
+                        self.bool_expr(e);
+                    }
+                },
+                _ => {},
+            }
         }
+        walk_expr(self, e);
     }
     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
         NestedVisitorMap::None
index a07cd5e5f4e53940be988f8fe12b8f968174d755..afb317421d0773aed78d0fdef2dc27089a6949d1 100644 (file)
@@ -30,6 +30,7 @@
     /// # let vec = vec![1_u8];
     /// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NAIVE_BYTECOUNT,
     pedantic,
     "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
@@ -73,10 +74,10 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
                     if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
                         &args[0]
                     } else {
-                        &filter_recv
+                        filter_recv
                     }
                 } else {
-                    &filter_recv
+                    filter_recv
                 };
                 let mut applicability = Applicability::MaybeIncorrect;
                 span_lint_and_sugg(
index ff619c59b6e2412da3d5ef45ddacb137848bebe6..23f79fdc68238ec24ecb1fe5dd2c6e7c08dd3279 100644 (file)
@@ -42,6 +42,7 @@
     /// keywords = ["clippy", "lint", "plugin"]
     /// categories = ["development-tools", "development-tools::cargo-plugins"]
     /// ```
+    #[clippy::version = "1.32.0"]
     pub CARGO_COMMON_METADATA,
     cargo,
     "common metadata is defined in `Cargo.toml`"
index c876553c165beb916409a9bb3a159a9d79974192..3f286dd9e2fca893b849ce17450b909caf4d3d02 100644 (file)
@@ -27,6 +27,7 @@
     ///     filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rs")) == Some(true)
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
     pedantic,
     "Checks for calls to ends_with with case-sensitive file extensions"
index 869deecfbd53a7ff882cb96d70f5dc2afa89ad00..4a95bed1148dc4f89df5069527f7d6c0d4b3f7c4 100644 (file)
@@ -1,16 +1,24 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_constant;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::is_isize_or_usize;
+use clippy_utils::{in_constant, meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, FloatTy, Ty};
+use rustc_semver::RustcVersion;
 
 use super::{utils, CAST_LOSSLESS};
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
-    if !should_lint(cx, expr, cast_from, cast_to) {
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    cast_op: &Expr<'_>,
+    cast_from: Ty<'_>,
+    cast_to: Ty<'_>,
+    msrv: &Option<RustcVersion>,
+) {
+    if !should_lint(cx, expr, cast_from, cast_to, msrv) {
         return;
     }
 
@@ -32,21 +40,36 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, c
         },
     );
 
+    let message = if cast_from.is_bool() {
+        format!(
+            "casting `{0:}` to `{1:}` is more cleanly stated with `{1:}::from(_)`",
+            cast_from, cast_to
+        )
+    } else {
+        format!(
+            "casting `{}` to `{}` may become silently lossy if you later change the type",
+            cast_from, cast_to
+        )
+    };
+
     span_lint_and_sugg(
         cx,
         CAST_LOSSLESS,
         expr.span,
-        &format!(
-            "casting `{}` to `{}` may become silently lossy if you later change the type",
-            cast_from, cast_to
-        ),
+        &message,
         "try",
         format!("{}::from({})", cast_to, sugg),
         applicability,
     );
 }
 
-fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) -> bool {
+fn should_lint(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    cast_from: Ty<'_>,
+    cast_to: Ty<'_>,
+    msrv: &Option<RustcVersion>,
+) -> bool {
     // Do not suggest using From in consts/statics until it is valid to do so (see #2267).
     if in_constant(cx, expr.hir_id) {
         return false;
@@ -72,7 +95,7 @@ fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to
             };
             from_nbits < to_nbits
         },
-
+        (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv.as_ref(), &msrvs::FROM_BOOL) => true,
         (_, _) => {
             matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
         },
index 233abd178943e9bc20b2685c68fd55f03d46a9c3..aee1e50b94a2a549d199daa74d19d7dabbfa0a69 100644 (file)
@@ -40,6 +40,7 @@
     /// let x = u64::MAX;
     /// x as f64;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_PRECISION_LOSS,
     pedantic,
     "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
@@ -61,6 +62,7 @@
     /// let y: i8 = -1;
     /// y as u128; // will return 18446744073709551615
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_SIGN_LOSS,
     pedantic,
     "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
@@ -83,6 +85,7 @@
     ///     x as u8
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_POSSIBLE_TRUNCATION,
     pedantic,
     "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
     /// ```rust
     /// u32::MAX as i32; // will yield a value of `-1`
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_POSSIBLE_WRAP,
     pedantic,
     "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
     ///     u64::from(x)
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_LOSSLESS,
     pedantic,
     "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
     /// let _ = 2_i32;
     /// let _ = 0.5_f32;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_CAST,
     complexity,
     "cast to the same type, e.g., `x as i32` where `x: i32`"
     /// (&1u8 as *const u8).cast::<u16>();
     /// (&mut 1u8 as *mut u8).cast::<u16>();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_PTR_ALIGNMENT,
     pedantic,
     "cast from a pointer to a more-strictly-aligned pointer"
     /// fn fun2() -> i32 { 1 }
     /// let a = fun2 as usize;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FN_TO_NUMERIC_CAST,
     style,
     "casting a function pointer to a numeric type other than usize"
     /// let fn_ptr = fn2 as usize;
     /// let fn_ptr_truncated = fn_ptr as i32;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
     style,
     "casting a function pointer to a numeric type not wide enough to store the address"
     /// }
     /// let _ = fn3 as fn() -> u16;
     /// ```
+    #[clippy::version = "1.58.0"]
     pub FN_TO_NUMERIC_CAST_ANY,
     restriction,
     "casting a function pointer to any integer type"
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.33.0"]
     pub CAST_REF_TO_MUT,
     correctness,
     "a cast of reference to a mutable pointer"
     /// ```rust,ignore
     /// b'x'
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CHAR_LIT_AS_U8,
     complexity,
     "casting a character literal to `u8` truncates"
     /// let _ = ptr.cast::<i32>();
     /// let _ = mut_ptr.cast::<i32>();
     /// ```
+    #[clippy::version = "1.51.0"]
     pub PTR_AS_PTR,
     pedantic,
     "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
@@ -426,12 +439,16 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
             fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
             fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
-            if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
-                cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
-                cast_possible_wrap::check(cx, expr, cast_from, cast_to);
-                cast_precision_loss::check(cx, expr, cast_from, cast_to);
-                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to);
-                cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
+
+            if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
+                if cast_from.is_numeric() {
+                    cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
+                    cast_possible_wrap::check(cx, expr, cast_from, cast_to);
+                    cast_precision_loss::check(cx, expr, cast_from, cast_to);
+                    cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
+                }
+
+                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
             }
         }
 
index 842bbf006cca979da67bbec6f2e42280508edd67..ffe6340bd77a58efe1ecf0588c306139b400b722 100644 (file)
@@ -36,6 +36,7 @@
     /// i32::try_from(foo).is_ok()
     /// # ;
     /// ```
+    #[clippy::version = "1.37.0"]
     pub CHECKED_CONVERSIONS,
     pedantic,
     "`try_from` could replace manual bounds checking when casting"
index 1ccb8c5d880f441388349d77e7475006182f117a..84a2373efe15b0adadd14d9361d18638ad084545 100644 (file)
@@ -27,6 +27,7 @@
     ///
     /// ### Example
     /// No. You'll see it when you get the warning.
+    #[clippy::version = "1.35.0"]
     pub COGNITIVE_COMPLEXITY,
     nursery,
     "functions that should be split up into multiple functions"
index 4aa8798071568d1c01f2f6a95663007d98c885f6..f03f34e5a4b381e0fc238a252dcafb5d914bf2f4 100644 (file)
@@ -47,6 +47,7 @@
     ///     …
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub COLLAPSIBLE_IF,
     style,
     "nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`"
@@ -82,6 +83,7 @@
     ///     …
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub COLLAPSIBLE_ELSE_IF,
     style,
     "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)"
index a4693fa213bc4a110470615ba12b9912754fe297..626f9971f01e7d678cbc69c22a00d09841109cf7 100644 (file)
@@ -41,6 +41,7 @@
     ///     };
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub COLLAPSIBLE_MATCH,
     style,
     "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
index 597a3c67024e547b74cb77911492d26aae6bf0e5..399d11472b09b2a6f49b8346c65a09cdb8368267 100644 (file)
@@ -49,6 +49,7 @@
     ///      }
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub COMPARISON_CHAIN,
     style,
     "`if`s that can be rewritten with `match` and `cmp`"
index 8abf10c0d1c2d174b1380b5a3c01d5ef5b17d3ee..d07bc23235b0f37bdadf0811097775129c8e3802 100644 (file)
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
 use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
 use clippy_utils::{
-    both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, is_else_clause,
-    is_lint_allowed, search_same, ContainsName, SpanlessEq, SpanlessHash,
+    both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, is_else_clause, is_lint_allowed,
+    search_same, ContainsName, SpanlessEq, SpanlessHash,
 };
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
@@ -41,6 +41,7 @@
     ///     …
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IFS_SAME_COND,
     correctness,
     "consecutive `if`s with the same condition"
@@ -88,6 +89,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.41.0"]
     pub SAME_FUNCTIONS_IN_IF_CONDITION,
     pedantic,
     "consecutive `if`s with the same function call"
     ///     42
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IF_SAME_THEN_ELSE,
     correctness,
     "`if` with the same `then` and `else` blocks"
     ///     42
     /// };
     /// ```
+    #[clippy::version = "1.53.0"]
     pub BRANCHES_SHARING_CODE,
     nursery,
     "`if` statement with shared code in all blocks"
@@ -623,7 +627,7 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
 
     let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool {
         // Do not lint if any expr originates from a macro
-        if in_macro(lhs.span) || in_macro(rhs.span) {
+        if lhs.span.from_expansion() || rhs.span.from_expansion() {
             return false;
         }
         // Do not spawn warning if `IFS_SAME_COND` already produced it.
index c2e9e8b3ab7f39d226f32df4449cfef0190cacbe..026683f6006246e040ee445bd8f910df5db5c38d 100644 (file)
@@ -28,6 +28,7 @@
     /// let a: Vec<_> = my_iterator.take(1).collect();
     /// let b: Vec<_> = my_iterator.collect();
     /// ```
+    #[clippy::version = "1.30.0"]
     pub COPY_ITERATOR,
     pedantic,
     "implementing `Iterator` on a `Copy` type"
index e4ee27724831d7e733ee52d2da5d4e42e71c9f1e..6bc4054a5abca5237a689d5d5c46ffa8e0c72b05 100644 (file)
@@ -23,6 +23,7 @@
     /// ```rust
     /// std::fs::create_dir_all("foo");
     /// ```
+    #[clippy::version = "1.48.0"]
     pub CREATE_DIR,
     restriction,
     "calling `std::fs::create_dir` instead of `std::fs::create_dir_all`"
index bab4a696f831ec859c986e74ffec7ec644c03343..5a0b60fdfbc01924de5a09c3dc36d62a6b8592c1 100644 (file)
     /// `dbg!` macro is intended as a debugging tool. It
     /// should not be in version control.
     ///
+    /// ### Known problems
+    /// * The lint level is unaffected by crate attributes. The level can still
+    ///   be set for functions, modules and other items. To change the level for
+    ///   the entire crate, please use command line flags. More information and a
+    ///   configuration example can be found in [clippy#6610].
+    ///
+    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
+    ///
     /// ### Example
     /// ```rust,ignore
     /// // Bad
@@ -23,6 +31,7 @@
     /// // Good
     /// true
     /// ```
+    #[clippy::version = "1.34.0"]
     pub DBG_MACRO,
     restriction,
     "`dbg!` macro is intended as a debugging tool"
index cde27d3ad2a0ce3def12b28ab0e5ebfddb640489..a0b137efe221a3bc8113a2936403bbce99f9b874 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_macro_callsite;
 use clippy_utils::ty::{has_drop, is_copy};
-use clippy_utils::{any_parent_is_automatically_derived, contains_name, in_macro, match_def_path, paths};
+use clippy_utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -29,6 +29,7 @@
     /// // Good
     /// let s = String::default();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DEFAULT_TRAIT_ACCESS,
     pedantic,
     "checks for literal calls to `Default::default()`"
@@ -62,6 +63,7 @@
     ///     .. Default::default()
     /// };
     /// ```
+    #[clippy::version = "1.49.0"]
     pub FIELD_REASSIGN_WITH_DEFAULT,
     style,
     "binding initialized with Default should have its fields set in the initializer"
@@ -78,7 +80,7 @@ pub struct Default {
 impl LateLintPass<'_> for Default {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
-            if !in_macro(expr.span);
+            if !expr.span.from_expansion();
             // Avoid cases already linted by `field_reassign_with_default`
             if !self.reassigned_linted.contains(&expr.span);
             if let ExprKind::Call(path, ..) = expr.kind;
@@ -125,7 +127,7 @@ fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
                 if let StmtKind::Local(local) = stmt.kind;
                 if let Some(expr) = local.init;
                 if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
-                if !in_macro(expr.span);
+                if !expr.span.from_expansion();
                 // only take bindings to identifiers
                 if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
                 // only when assigning `... = Default::default()`
index 3f1b7ea6214d4fb969bf3ccb412c46d2d713f367..3573ea5f02671becbce4b4f1ddd00559fe9c84c9 100644 (file)
@@ -46,6 +46,7 @@
     /// let i = 10i32;
     /// let f = 1.23f64;
     /// ```
+    #[clippy::version = "1.52.0"]
     pub DEFAULT_NUMERIC_FALLBACK,
     restriction,
     "usage of unconstrained numeric literals which may cause default numeric fallback."
index 9d8524ec91cc6e08f7c2ea056aabb58f3103e02e..bba27576c892737232f7497187caf56e73c2c179 100644 (file)
@@ -21,6 +21,7 @@ macro_rules! declare_deprecated_lint {
     /// ### Deprecation reason
     /// This used to check for `assert!(a == b)` and recommend
     /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011.
+    #[clippy::version = "pre 1.29.0"]
     pub SHOULD_ASSERT_EQ,
     "`assert!()` will be more flexible with RFC 2011"
 }
@@ -32,6 +33,7 @@ macro_rules! declare_deprecated_lint {
     /// ### Deprecation reason
     /// This used to check for `Vec::extend`, which was slower than
     /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true.
+    #[clippy::version = "pre 1.29.0"]
     pub EXTEND_FROM_SLICE,
     "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice"
 }
@@ -45,6 +47,7 @@ macro_rules! declare_deprecated_lint {
     /// an infinite iterator, which is better expressed by `iter::repeat`,
     /// but the method has been removed for `Iterator::step_by` which panics
     /// if given a zero
+    #[clippy::version = "pre 1.29.0"]
     pub RANGE_STEP_BY_ZERO,
     "`iterator.step_by(0)` panics nowadays"
 }
@@ -56,6 +59,7 @@ macro_rules! declare_deprecated_lint {
     /// ### Deprecation reason
     /// This used to check for `Vec::as_slice`, which was unstable with good
     /// stable alternatives. `Vec::as_slice` has now been stabilized.
+    #[clippy::version = "pre 1.29.0"]
     pub UNSTABLE_AS_SLICE,
     "`Vec::as_slice` has been stabilized in 1.7"
 }
@@ -67,6 +71,7 @@ macro_rules! declare_deprecated_lint {
     /// ### Deprecation reason
     /// This used to check for `Vec::as_mut_slice`, which was unstable with good
     /// stable alternatives. `Vec::as_mut_slice` has now been stabilized.
+    #[clippy::version = "pre 1.29.0"]
     pub UNSTABLE_AS_MUT_SLICE,
     "`Vec::as_mut_slice` has been stabilized in 1.7"
 }
@@ -80,6 +85,7 @@ macro_rules! declare_deprecated_lint {
     /// between non-pointer types of differing alignment is well-defined behavior (it's semantically
     /// equivalent to a memcpy). This lint has thus been refactored into two separate lints:
     /// cast_ptr_alignment and transmute_ptr_to_ptr.
+    #[clippy::version = "pre 1.29.0"]
     pub MISALIGNED_TRANSMUTE,
     "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr"
 }
@@ -92,6 +98,7 @@ macro_rules! declare_deprecated_lint {
     /// This lint is too subjective, not having a good reason for being in clippy.
     /// Additionally, compound assignment operators may be overloaded separately from their non-assigning
     /// counterparts, so this lint may suggest a change in behavior or the code may not compile.
+    #[clippy::version = "1.30.0"]
     pub ASSIGN_OPS,
     "using compound assignment operators (e.g., `+=`) is harmless"
 }
@@ -104,6 +111,7 @@ macro_rules! declare_deprecated_lint {
     /// The original rule will only lint for `if let`. After
     /// making it support to lint `match`, naming as `if let` is not suitable for it.
     /// So, this lint is deprecated.
+    #[clippy::version = "pre 1.29.0"]
     pub IF_LET_REDUNDANT_PATTERN_MATCHING,
     "this lint has been changed to redundant_pattern_matching"
 }
@@ -117,6 +125,7 @@ macro_rules! declare_deprecated_lint {
     /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The
     /// replacement has very different performance characteristics so the lint is
     /// deprecated.
+    #[clippy::version = "pre 1.29.0"]
     pub UNSAFE_VECTOR_INITIALIZATION,
     "the replacement suggested by this lint had substantially different behavior"
 }
@@ -127,6 +136,7 @@ macro_rules! declare_deprecated_lint {
     ///
     /// ### Deprecation reason
     /// This lint has been superseded by #[must_use] in rustc.
+    #[clippy::version = "1.39.0"]
     pub UNUSED_COLLECT,
     "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
 }
@@ -137,6 +147,7 @@ macro_rules! declare_deprecated_lint {
     ///
     /// ### Deprecation reason
     /// Associated-constants are now preferred.
+    #[clippy::version = "1.44.0"]
     pub REPLACE_CONSTS,
     "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants"
 }
@@ -147,6 +158,7 @@ macro_rules! declare_deprecated_lint {
     ///
     /// ### Deprecation reason
     /// The regex! macro does not exist anymore.
+    #[clippy::version = "1.47.0"]
     pub REGEX_MACRO,
     "the regex! macro has been removed from the regex crate in 2018"
 }
@@ -158,6 +170,7 @@ macro_rules! declare_deprecated_lint {
     /// ### Deprecation reason
     /// This lint has been replaced by `manual_find_map`, a
     /// more specific lint.
+    #[clippy::version = "1.51.0"]
     pub FIND_MAP,
     "this lint has been replaced by `manual_find_map`, a more specific lint"
 }
@@ -169,6 +182,7 @@ macro_rules! declare_deprecated_lint {
     /// ### Deprecation reason
     /// This lint has been replaced by `manual_filter_map`, a
     /// more specific lint.
+    #[clippy::version = "1.53.0"]
     pub FILTER_MAP,
     "this lint has been replaced by `manual_filter_map`, a more specific lint"
 }
@@ -181,6 +195,7 @@ macro_rules! declare_deprecated_lint {
     /// The `avoid_breaking_exported_api` config option was added, which
     /// enables the `enum_variant_names` lint for public items.
     /// ```
+    #[clippy::version = "1.54.0"]
     pub PUB_ENUM_VARIANT_NAMES,
     "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items"
 }
@@ -192,6 +207,7 @@ macro_rules! declare_deprecated_lint {
     /// ### Deprecation reason
     /// The `avoid_breaking_exported_api` config option was added, which
     /// enables the `wrong_self_conversion` lint for public items.
+    #[clippy::version = "1.54.0"]
     pub WRONG_PUB_SELF_CONVENTION,
     "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items"
 }
index ce59311c4aa96acac5154ce00f02e3d0a746fa99..fa2b348591be4d7a0118095a99734f9297f60900 100644 (file)
@@ -1,14 +1,20 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_context;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::ty::peel_mid_ty_refs;
-use clippy_utils::{get_parent_node, in_macro, is_lint_allowed};
-use rustc_ast::util::parser::PREC_PREFIX;
+use clippy_utils::{get_parent_expr, get_parent_node, is_lint_allowed, path_to_local};
+use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp};
+use rustc_hir::{
+    BindingAnnotation, Body, BodyId, BorrowKind, Destination, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
+    Pat, PatKind, UnOp,
+};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{symbol::sym, Span};
+use std::iter;
 
 declare_clippy_lint! {
     /// ### What it does
     /// ```rust,ignore
     /// let _ = d.unwrap().deref();
     /// ```
+    #[clippy::version = "1.44.0"]
     pub EXPLICIT_DEREF_METHODS,
     pedantic,
     "Explicit use of deref or deref_mut method while not in a method chain."
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for address of operations (`&`) that are going to
+    /// be dereferenced immediately by the compiler.
+    ///
+    /// ### Why is this bad?
+    /// Suggests that the receiver of the expression borrows
+    /// the expression.
+    ///
+    /// ### Example
+    /// ```rust
+    /// fn fun(_a: &i32) {}
+    ///
+    /// // Bad
+    /// let x: &i32 = &&&&&&5;
+    /// fun(&x);
+    ///
+    /// // Good
+    /// let x: &i32 = &5;
+    /// fun(x);
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub NEEDLESS_BORROW,
+    style,
+    "taking a reference that is going to be automatically dereferenced"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `ref` bindings which create a reference to a reference.
+    ///
+    /// ### Why is this bad?
+    /// The address-of operator at the use site is clearer about the need for a reference.
+    ///
+    /// ### Example
+    /// ```rust
+    /// // Bad
+    /// let x = Some("");
+    /// if let Some(ref x) = x {
+    ///     // use `x` here
+    /// }
+    ///
+    /// // Good
+    /// let x = Some("");
+    /// if let Some(x) = x {
+    ///     // use `&x` here
+    /// }
+    /// ```
+    #[clippy::version = "1.54.0"]
+    pub REF_BINDING_TO_REFERENCE,
+    pedantic,
+    "`ref` binding to a reference"
+}
+
 impl_lint_pass!(Dereferencing => [
     EXPLICIT_DEREF_METHODS,
+    NEEDLESS_BORROW,
+    REF_BINDING_TO_REFERENCE,
 ]);
 
 #[derive(Default)]
@@ -51,6 +114,18 @@ pub struct Dereferencing {
     // expression. This is to store the id of that expression so it can be skipped when
     // `check_expr` is called for it.
     skip_expr: Option<HirId>,
+
+    /// The body the first local was found in. Used to emit lints when the traversal of the body has
+    /// been finished. Note we can't lint at the end of every body as they can be nested within each
+    /// other.
+    current_body: Option<BodyId>,
+    /// The list of locals currently being checked by the lint.
+    /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
+    /// This is needed for or patterns where one of the branches can be linted, but another can not
+    /// be.
+    ///
+    /// e.g. `m!(x) | Foo::Bar(ref x)`
+    ref_locals: FxIndexMap<HirId, Option<RefPat>>,
 }
 
 struct StateData {
@@ -67,6 +142,9 @@ enum State {
         ty_changed_count: usize,
         is_final_ufcs: bool,
     },
+    DerefedBorrow {
+        count: u32,
+    },
 }
 
 // A reference operation considered by this lint pass
@@ -76,15 +154,31 @@ enum RefOp {
     AddrOf,
 }
 
+struct RefPat {
+    /// Whether every usage of the binding is dereferenced.
+    always_deref: bool,
+    /// The spans of all the ref bindings for this local.
+    spans: Vec<Span>,
+    /// The applicability of this suggestion.
+    app: Applicability,
+    /// All the replacements which need to be made.
+    replacements: Vec<(Span, String)>,
+}
+
 impl<'tcx> LateLintPass<'tcx> for Dereferencing {
+    #[allow(clippy::too_many_lines)]
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         // Skip path expressions from deref calls. e.g. `Deref::deref(e)`
         if Some(expr.hir_id) == self.skip_expr.take() {
             return;
         }
 
+        if let Some(local) = path_to_local(expr) {
+            self.check_local_usage(cx, expr, local);
+        }
+
         // Stop processing sub expressions when a macro call is seen
-        if in_macro(expr.span) {
+        if expr.span.from_expansion() {
             if let Some((state, data)) = self.state.take() {
                 report(cx, expr, state, data);
             }
@@ -127,6 +221,48 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                             },
                         ));
                     },
+                    RefOp::AddrOf => {
+                        // Find the number of times the borrow is auto-derefed.
+                        let mut iter = find_adjustments(cx.tcx, typeck, expr).iter();
+                        if let Some((i, adjust)) = iter.by_ref().enumerate().find_map(|(i, adjust)| {
+                            if !matches!(adjust.kind, Adjust::Deref(_)) {
+                                Some((i, adjust))
+                            } else if !adjust.target.is_ref() {
+                                // Add one to the number of references found.
+                                Some((i + 1, adjust))
+                            } else {
+                                None
+                            }
+                        }) {
+                            // Found two consecutive derefs. At least one can be removed.
+                            if i > 1 {
+                                let target_mut = iter::once(adjust)
+                                    .chain(iter)
+                                    .find_map(|adjust| match adjust.kind {
+                                        Adjust::Borrow(AutoBorrow::Ref(_, m)) => Some(m.into()),
+                                        _ => None,
+                                    })
+                                    // This default should never happen. Auto-deref always reborrows.
+                                    .unwrap_or(Mutability::Not);
+                                self.state = Some((
+                                    // Subtract one for the current borrow expression, and one to cover the last
+                                    // reference which can't be removed (it's either reborrowed, or needed for
+                                    // auto-deref to happen).
+                                    State::DerefedBorrow {
+                                        count:
+                                            // Truncation here would require more than a `u32::MAX` level reference. The compiler
+                                            // does not support this.
+                                            #[allow(clippy::cast_possible_truncation)]
+                                            { i as u32 - 2 }
+                                    },
+                                    StateData {
+                                        span: expr.span,
+                                        target_mut,
+                                    },
+                                ));
+                            }
+                        }
+                    },
                     _ => (),
                 }
             },
@@ -143,10 +279,80 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     data,
                 ));
             },
+            (Some((State::DerefedBorrow { count }, data)), RefOp::AddrOf) if count != 0 => {
+                self.state = Some((State::DerefedBorrow { count: count - 1 }, data));
+            },
 
             (Some((state, data)), _) => report(cx, expr, state, data),
         }
     }
+
+    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
+        if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind {
+            if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
+                // This binding id has been seen before. Add this pattern to the list of changes.
+                if let Some(prev_pat) = opt_prev_pat {
+                    if pat.span.from_expansion() {
+                        // Doesn't match the context of the previous pattern. Can't lint here.
+                        *opt_prev_pat = None;
+                    } else {
+                        prev_pat.spans.push(pat.span);
+                        prev_pat.replacements.push((
+                            pat.span,
+                            snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
+                                .0
+                                .into(),
+                        ));
+                    }
+                }
+                return;
+            }
+
+            if_chain! {
+                if !pat.span.from_expansion();
+                if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
+                // only lint immutable refs, because borrowed `&mut T` cannot be moved out
+                if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
+                then {
+                    let mut app = Applicability::MachineApplicable;
+                    let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
+                    self.current_body = self.current_body.or(cx.enclosing_body);
+                    self.ref_locals.insert(
+                        id,
+                        Some(RefPat {
+                            always_deref: true,
+                            spans: vec![pat.span],
+                            app,
+                            replacements: vec![(pat.span, snip.into())],
+                        }),
+                    );
+                }
+            }
+        }
+    }
+
+    fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
+        if Some(body.id()) == self.current_body {
+            for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
+                let replacements = pat.replacements;
+                let app = pat.app;
+                span_lint_and_then(
+                    cx,
+                    if pat.always_deref {
+                        NEEDLESS_BORROW
+                    } else {
+                        REF_BINDING_TO_REFERENCE
+                    },
+                    pat.spans,
+                    "this pattern creates a reference to a reference",
+                    |diag| {
+                        diag.multipart_suggestion("try this", replacements, app);
+                    },
+                );
+            }
+            self.current_body = None;
+        }
+    }
 }
 
 fn try_parse_ref_op(
@@ -250,6 +456,48 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId,
     }
 }
 
+/// Adjustments are sometimes made in the parent block rather than the expression itself.
+fn find_adjustments(
+    tcx: TyCtxt<'tcx>,
+    typeck: &'tcx TypeckResults<'_>,
+    expr: &'tcx Expr<'_>,
+) -> &'tcx [Adjustment<'tcx>] {
+    let map = tcx.hir();
+    let mut iter = map.parent_iter(expr.hir_id);
+    let mut prev = expr;
+
+    loop {
+        match typeck.expr_adjustments(prev) {
+            [] => (),
+            a => break a,
+        };
+
+        match iter.next().map(|(_, x)| x) {
+            Some(Node::Block(_)) => {
+                if let Some((_, Node::Expr(e))) = iter.next() {
+                    prev = e;
+                } else {
+                    // This shouldn't happen. Blocks are always contained in an expression.
+                    break &[];
+                }
+            },
+            Some(Node::Expr(&Expr {
+                kind: ExprKind::Break(Destination { target_id: Ok(id), .. }, _),
+                ..
+            })) => {
+                if let Some(Node::Expr(e)) = map.find(id) {
+                    prev = e;
+                    iter = map.parent_iter(id);
+                } else {
+                    // This shouldn't happen. The destination should exist.
+                    break &[];
+                }
+            },
+            _ => break &[],
+        }
+    }
+}
+
 #[allow(clippy::needless_pass_by_value)]
 fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
     match state {
@@ -300,5 +548,83 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat
                 app,
             );
         },
+        State::DerefedBorrow { .. } => {
+            let mut app = Applicability::MachineApplicable;
+            let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
+            span_lint_and_sugg(
+                cx,
+                NEEDLESS_BORROW,
+                data.span,
+                &format!(
+                    "this expression borrows a reference (`{}`) that is immediately dereferenced by the compiler",
+                    cx.typeck_results().expr_ty(expr),
+                ),
+                "change this to",
+                snip.into(),
+                app,
+            );
+        },
+    }
+}
+
+impl Dereferencing {
+    fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) {
+        if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
+            if let Some(pat) = outer_pat {
+                // Check for auto-deref
+                if !matches!(
+                    cx.typeck_results().expr_adjustments(e),
+                    [
+                        Adjustment {
+                            kind: Adjust::Deref(_),
+                            ..
+                        },
+                        Adjustment {
+                            kind: Adjust::Deref(_),
+                            ..
+                        },
+                        ..
+                    ]
+                ) {
+                    match get_parent_expr(cx, e) {
+                        // Field accesses are the same no matter the number of references.
+                        Some(Expr {
+                            kind: ExprKind::Field(..),
+                            ..
+                        }) => (),
+                        Some(&Expr {
+                            span,
+                            kind: ExprKind::Unary(UnOp::Deref, _),
+                            ..
+                        }) if !span.from_expansion() => {
+                            // Remove explicit deref.
+                            let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
+                            pat.replacements.push((span, snip.into()));
+                        },
+                        Some(parent) if !parent.span.from_expansion() => {
+                            // Double reference might be needed at this point.
+                            if parent.precedence().order() == PREC_POSTFIX {
+                                // Parentheses would be needed here, don't lint.
+                                *outer_pat = None;
+                            } else {
+                                pat.always_deref = false;
+                                let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
+                                pat.replacements.push((e.span, format!("&{}", snip)));
+                            }
+                        },
+                        _ if !e.span.from_expansion() => {
+                            // Double reference might be needed at this point.
+                            pat.always_deref = false;
+                            let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
+                            pat.replacements.push((e.span, format!("&{}", snip)));
+                        },
+                        // Edge case for macros. The span of the identifier will usually match the context of the
+                        // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
+                        // macros
+                        _ => *outer_pat = None,
+                    }
+                }
+            }
+        }
     }
 }
index fdef0abe9708e34eb7d0f800d4bbc1b10d4b54fe..d0fab2b48fb074beba10b3e7b864d64eaab74549 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{in_macro, is_automatically_derived, is_default_equivalent, remove_blocks};
+use clippy_utils::{is_automatically_derived, is_default_equivalent, remove_blocks};
 use rustc_hir::{
     def::{DefKind, Res},
     Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
@@ -46,6 +46,7 @@
     /// has exactly equal bounds, and therefore this lint is disabled for types with
     /// generic parameters.
     ///
+    #[clippy::version = "1.57.0"]
     pub DERIVABLE_IMPLS,
     complexity,
     "manual implementation of the `Default` trait which is equal to a derive"
@@ -72,7 +73,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
             }) = item.kind;
             if let attrs = cx.tcx.hir().attrs(item.hir_id());
             if !is_automatically_derived(attrs);
-            if !in_macro(item.span);
+            if !item.span.from_expansion();
             if let Some(def_id) = trait_ref.trait_def_id();
             if cx.tcx.is_diagnostic_item(sym::Default, def_id);
             if let impl_item_hir = child.id.hir_id();
index 94b35ad88af2d605243fb06db7379633d43bb15b..097cb65f56e4e8c63b760cb56bfb32aa30a59a0e 100644 (file)
@@ -37,6 +37,7 @@
     ///     ...
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DERIVE_HASH_XOR_EQ,
     correctness,
     "deriving `Hash` but implementing `PartialEq` explicitly"
@@ -87,6 +88,7 @@
     /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
     /// struct Foo;
     /// ```
+    #[clippy::version = "1.47.0"]
     pub DERIVE_ORD_XOR_PARTIAL_ORD,
     correctness,
     "deriving `Ord` but implementing `PartialOrd` explicitly"
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPL_IMPL_CLONE_ON_COPY,
     pedantic,
     "implementing `Clone` explicitly on `Copy` types"
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub UNSAFE_DERIVE_DESERIALIZE,
     pedantic,
     "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_method.rs b/src/tools/clippy/clippy_lints/src/disallowed_method.rs
deleted file mode 100644 (file)
index 22d726c..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::fn_def_id;
-
-use rustc_hir::{def::Res, def_id::DefIdMap, Expr};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-
-use crate::utils::conf;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Denies the configured methods and functions in clippy.toml
-    ///
-    /// ### Why is this bad?
-    /// Some methods are undesirable in certain contexts, and it's beneficial to
-    /// lint for them as needed.
-    ///
-    /// ### Example
-    /// An example clippy.toml configuration:
-    /// ```toml
-    /// # clippy.toml
-    /// disallowed-methods = [
-    ///     # Can use a string as the path of the disallowed method.
-    ///     "std::boxed::Box::new",
-    ///     # Can also use an inline table with a `path` key.
-    ///     { path = "std::time::Instant::now" },
-    ///     # When using an inline table, can add a `reason` for why the method
-    ///     # is disallowed.
-    ///     { path = "std::vec::Vec::leak", reason = "no leaking memory" },
-    /// ]
-    /// ```
-    ///
-    /// ```rust,ignore
-    /// // Example code where clippy issues a warning
-    /// let xs = vec![1, 2, 3, 4];
-    /// xs.leak(); // Vec::leak is disallowed in the config.
-    /// // The diagnostic contains the message "no leaking memory".
-    ///
-    /// let _now = Instant::now(); // Instant::now is disallowed in the config.
-    ///
-    /// let _box = Box::new(3); // Box::new is disallowed in the config.
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust,ignore
-    /// // Example code which does not raise clippy warning
-    /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.
-    /// xs.push(123); // Vec::push is _not_ disallowed in the config.
-    /// ```
-    pub DISALLOWED_METHOD,
-    nursery,
-    "use of a disallowed method call"
-}
-
-#[derive(Clone, Debug)]
-pub struct DisallowedMethod {
-    conf_disallowed: Vec<conf::DisallowedMethod>,
-    disallowed: DefIdMap<Option<String>>,
-}
-
-impl DisallowedMethod {
-    pub fn new(conf_disallowed: Vec<conf::DisallowedMethod>) -> Self {
-        Self {
-            conf_disallowed,
-            disallowed: DefIdMap::default(),
-        }
-    }
-}
-
-impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
-
-impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
-    fn check_crate(&mut self, cx: &LateContext<'_>) {
-        for conf in &self.conf_disallowed {
-            let (path, reason) = match conf {
-                conf::DisallowedMethod::Simple(path) => (path, None),
-                conf::DisallowedMethod::WithReason { path, reason } => (
-                    path,
-                    reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
-                ),
-            };
-            let segs: Vec<_> = path.split("::").collect();
-            if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) {
-                self.disallowed.insert(id, reason);
-            }
-        }
-    }
-
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let def_id = match fn_def_id(cx, expr) {
-            Some(def_id) => def_id,
-            None => return,
-        };
-        let reason = match self.disallowed.get(&def_id) {
-            Some(reason) => reason,
-            None => return,
-        };
-        let func_path = cx.tcx.def_path_str(def_id);
-        let msg = format!("use of a disallowed method `{}`", func_path);
-        span_lint_and_then(cx, DISALLOWED_METHOD, expr.span, &msg, |diag| {
-            if let Some(reason) = reason {
-                diag.note(reason);
-            }
-        });
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
new file mode 100644 (file)
index 0000000..6d40659
--- /dev/null
@@ -0,0 +1,107 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::fn_def_id;
+
+use rustc_hir::{def::Res, def_id::DefIdMap, Expr};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+use crate::utils::conf;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Denies the configured methods and functions in clippy.toml
+    ///
+    /// ### Why is this bad?
+    /// Some methods are undesirable in certain contexts, and it's beneficial to
+    /// lint for them as needed.
+    ///
+    /// ### Example
+    /// An example clippy.toml configuration:
+    /// ```toml
+    /// # clippy.toml
+    /// disallowed-methods = [
+    ///     # Can use a string as the path of the disallowed method.
+    ///     "std::boxed::Box::new",
+    ///     # Can also use an inline table with a `path` key.
+    ///     { path = "std::time::Instant::now" },
+    ///     # When using an inline table, can add a `reason` for why the method
+    ///     # is disallowed.
+    ///     { path = "std::vec::Vec::leak", reason = "no leaking memory" },
+    /// ]
+    /// ```
+    ///
+    /// ```rust,ignore
+    /// // Example code where clippy issues a warning
+    /// let xs = vec![1, 2, 3, 4];
+    /// xs.leak(); // Vec::leak is disallowed in the config.
+    /// // The diagnostic contains the message "no leaking memory".
+    ///
+    /// let _now = Instant::now(); // Instant::now is disallowed in the config.
+    ///
+    /// let _box = Box::new(3); // Box::new is disallowed in the config.
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust,ignore
+    /// // Example code which does not raise clippy warning
+    /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.
+    /// xs.push(123); // Vec::push is _not_ disallowed in the config.
+    /// ```
+    #[clippy::version = "1.49.0"]
+    pub DISALLOWED_METHODS,
+    nursery,
+    "use of a disallowed method call"
+}
+
+#[derive(Clone, Debug)]
+pub struct DisallowedMethods {
+    conf_disallowed: Vec<conf::DisallowedMethod>,
+    disallowed: DefIdMap<Option<String>>,
+}
+
+impl DisallowedMethods {
+    pub fn new(conf_disallowed: Vec<conf::DisallowedMethod>) -> Self {
+        Self {
+            conf_disallowed,
+            disallowed: DefIdMap::default(),
+        }
+    }
+}
+
+impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
+
+impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
+    fn check_crate(&mut self, cx: &LateContext<'_>) {
+        for conf in &self.conf_disallowed {
+            let (path, reason) = match conf {
+                conf::DisallowedMethod::Simple(path) => (path, None),
+                conf::DisallowedMethod::WithReason { path, reason } => (
+                    path,
+                    reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
+                ),
+            };
+            let segs: Vec<_> = path.split("::").collect();
+            if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) {
+                self.disallowed.insert(id, reason);
+            }
+        }
+    }
+
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        let def_id = match fn_def_id(cx, expr) {
+            Some(def_id) => def_id,
+            None => return,
+        };
+        let reason = match self.disallowed.get(&def_id) {
+            Some(reason) => reason,
+            None => return,
+        };
+        let func_path = cx.tcx.def_path_str(def_id);
+        let msg = format!("use of a disallowed method `{}`", func_path);
+        span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| {
+            if let Some(reason) = reason {
+                diag.note(reason);
+            }
+        });
+    }
+}
index 6d38d30cd0bab5f63804b3509e373ffd1d0bc67c..3c3f3631849e58dc1b44a8da92edf70d84dff02d 100644 (file)
@@ -38,6 +38,7 @@
     /// let zähler = 10; // OK, it's still latin.
     /// let カウンタ = 10; // Will spawn the lint.
     /// ```
+    #[clippy::version = "1.55.0"]
     pub DISALLOWED_SCRIPT_IDENTS,
     restriction,
     "usage of non-allowed Unicode scripts"
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_type.rs b/src/tools/clippy/clippy_lints/src/disallowed_type.rs
deleted file mode 100644 (file)
index 48f7815..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::{
-    def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, TraitBoundModifier, Ty, TyKind, UseKind,
-};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
-
-use crate::utils::conf;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Denies the configured types in clippy.toml.
-    ///
-    /// ### Why is this bad?
-    /// Some types are undesirable in certain contexts.
-    ///
-    /// ### Example:
-    /// An example clippy.toml configuration:
-    /// ```toml
-    /// # clippy.toml
-    /// disallowed-types = [
-    ///     # Can use a string as the path of the disallowed type.
-    ///     "std::collections::BTreeMap",
-    ///     # Can also use an inline table with a `path` key.
-    ///     { path = "std::net::TcpListener" },
-    ///     # When using an inline table, can add a `reason` for why the type
-    ///     # is disallowed.
-    ///     { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
-    /// ]
-    /// ```
-    ///
-    /// ```rust,ignore
-    /// use std::collections::BTreeMap;
-    /// // or its use
-    /// let x = std::collections::BTreeMap::new();
-    /// ```
-    /// Use instead:
-    /// ```rust,ignore
-    /// // A similar type that is allowed by the config
-    /// use std::collections::HashMap;
-    /// ```
-    pub DISALLOWED_TYPE,
-    nursery,
-    "use of a disallowed type"
-}
-#[derive(Clone, Debug)]
-pub struct DisallowedType {
-    conf_disallowed: Vec<conf::DisallowedType>,
-    def_ids: FxHashMap<DefId, Option<String>>,
-    prim_tys: FxHashMap<PrimTy, Option<String>>,
-}
-
-impl DisallowedType {
-    pub fn new(conf_disallowed: Vec<conf::DisallowedType>) -> Self {
-        Self {
-            conf_disallowed,
-            def_ids: FxHashMap::default(),
-            prim_tys: FxHashMap::default(),
-        }
-    }
-
-    fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
-        match res {
-            Res::Def(_, did) => {
-                if let Some(reason) = self.def_ids.get(did) {
-                    emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref());
-                }
-            },
-            Res::PrimTy(prim) => {
-                if let Some(reason) = self.prim_tys.get(prim) {
-                    emit(cx, prim.name_str(), span, reason.as_deref());
-                }
-            },
-            _ => {},
-        }
-    }
-}
-
-impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]);
-
-impl<'tcx> LateLintPass<'tcx> for DisallowedType {
-    fn check_crate(&mut self, cx: &LateContext<'_>) {
-        for conf in &self.conf_disallowed {
-            let (path, reason) = match conf {
-                conf::DisallowedType::Simple(path) => (path, None),
-                conf::DisallowedType::WithReason { path, reason } => (
-                    path,
-                    reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
-                ),
-            };
-            let segs: Vec<_> = path.split("::").collect();
-            match clippy_utils::path_to_res(cx, &segs) {
-                Res::Def(_, id) => {
-                    self.def_ids.insert(id, reason);
-                },
-                Res::PrimTy(ty) => {
-                    self.prim_tys.insert(ty, reason);
-                },
-                _ => {},
-            }
-        }
-    }
-
-    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
-        if let ItemKind::Use(path, UseKind::Single) = &item.kind {
-            self.check_res_emit(cx, &path.res, item.span);
-        }
-    }
-
-    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
-        if let TyKind::Path(path) = &ty.kind {
-            self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span);
-        }
-    }
-
-    fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) {
-        self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span);
-    }
-}
-
-fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) {
-    span_lint_and_then(
-        cx,
-        DISALLOWED_TYPE,
-        span,
-        &format!("`{}` is not allowed according to config", name),
-        |diag| {
-            if let Some(reason) = reason {
-                diag.note(reason);
-            }
-        },
-    );
-}
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs
new file mode 100644 (file)
index 0000000..eaed403
--- /dev/null
@@ -0,0 +1,137 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::{
+    def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, TraitBoundModifier, Ty, TyKind, UseKind,
+};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Span;
+
+use crate::utils::conf;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Denies the configured types in clippy.toml.
+    ///
+    /// ### Why is this bad?
+    /// Some types are undesirable in certain contexts.
+    ///
+    /// ### Example:
+    /// An example clippy.toml configuration:
+    /// ```toml
+    /// # clippy.toml
+    /// disallowed-types = [
+    ///     # Can use a string as the path of the disallowed type.
+    ///     "std::collections::BTreeMap",
+    ///     # Can also use an inline table with a `path` key.
+    ///     { path = "std::net::TcpListener" },
+    ///     # When using an inline table, can add a `reason` for why the type
+    ///     # is disallowed.
+    ///     { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
+    /// ]
+    /// ```
+    ///
+    /// ```rust,ignore
+    /// use std::collections::BTreeMap;
+    /// // or its use
+    /// let x = std::collections::BTreeMap::new();
+    /// ```
+    /// Use instead:
+    /// ```rust,ignore
+    /// // A similar type that is allowed by the config
+    /// use std::collections::HashMap;
+    /// ```
+    #[clippy::version = "1.55.0"]
+    pub DISALLOWED_TYPES,
+    nursery,
+    "use of disallowed types"
+}
+#[derive(Clone, Debug)]
+pub struct DisallowedTypes {
+    conf_disallowed: Vec<conf::DisallowedType>,
+    def_ids: FxHashMap<DefId, Option<String>>,
+    prim_tys: FxHashMap<PrimTy, Option<String>>,
+}
+
+impl DisallowedTypes {
+    pub fn new(conf_disallowed: Vec<conf::DisallowedType>) -> Self {
+        Self {
+            conf_disallowed,
+            def_ids: FxHashMap::default(),
+            prim_tys: FxHashMap::default(),
+        }
+    }
+
+    fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
+        match res {
+            Res::Def(_, did) => {
+                if let Some(reason) = self.def_ids.get(did) {
+                    emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref());
+                }
+            },
+            Res::PrimTy(prim) => {
+                if let Some(reason) = self.prim_tys.get(prim) {
+                    emit(cx, prim.name_str(), span, reason.as_deref());
+                }
+            },
+            _ => {},
+        }
+    }
+}
+
+impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
+
+impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
+    fn check_crate(&mut self, cx: &LateContext<'_>) {
+        for conf in &self.conf_disallowed {
+            let (path, reason) = match conf {
+                conf::DisallowedType::Simple(path) => (path, None),
+                conf::DisallowedType::WithReason { path, reason } => (
+                    path,
+                    reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
+                ),
+            };
+            let segs: Vec<_> = path.split("::").collect();
+            match clippy_utils::path_to_res(cx, &segs) {
+                Res::Def(_, id) => {
+                    self.def_ids.insert(id, reason);
+                },
+                Res::PrimTy(ty) => {
+                    self.prim_tys.insert(ty, reason);
+                },
+                _ => {},
+            }
+        }
+    }
+
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+        if let ItemKind::Use(path, UseKind::Single) = &item.kind {
+            self.check_res_emit(cx, &path.res, item.span);
+        }
+    }
+
+    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
+        if let TyKind::Path(path) = &ty.kind {
+            self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span);
+        }
+    }
+
+    fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) {
+        self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span);
+    }
+}
+
+fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) {
+    span_lint_and_then(
+        cx,
+        DISALLOWED_TYPES,
+        span,
+        &format!("`{}` is not allowed according to config", name),
+        |diag| {
+            if let Some(reason) = reason {
+                diag.note(reason);
+            }
+        },
+    );
+}
index d4ba072807f8f891adb1de3f37b3327d5b9ea0eb..2cdd59c569198755ef24160c9c1e4f7295c52a00 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::attrs::is_doc_hidden;
-use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then};
 use clippy_utils::source::{first_line_of_span, snippet_with_applicability};
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
@@ -10,7 +10,7 @@
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::emitter::EmitterWriter;
-use rustc_errors::{Applicability, Handler};
+use rustc_errors::{Applicability, Handler, SuggestionStyle};
 use rustc_hir as hir;
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_hir::{AnonConst, Expr, ExprKind, QPath};
@@ -67,6 +67,7 @@
     /// /// [SmallVec]: SmallVec
     /// fn main() {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DOC_MARKDOWN,
     pedantic,
     "presence of `_`, `::` or camel-case outside backticks in documentation"
     ///     unimplemented!();
     /// }
     /// ```
+    #[clippy::version = "1.39.0"]
     pub MISSING_SAFETY_DOC,
     style,
     "`pub unsafe fn` without `# Safety` docs"
     ///     unimplemented!();
     /// }
     /// ```
+    #[clippy::version = "1.41.0"]
     pub MISSING_ERRORS_DOC,
     pedantic,
     "`pub fn` returns `Result` without `# Errors` in doc comment"
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.52.0"]
     pub MISSING_PANICS_DOC,
     pedantic,
     "`pub fn` may panic without `# Panics` in doc comment"
     ///     unimplemented!();
     /// }
     /// ``````
+    #[clippy::version = "1.40.0"]
     pub NEEDLESS_DOCTEST_MAIN,
     style,
     "presence of `fn main() {` in code examples"
@@ -639,7 +644,9 @@ fn has_needless_main(code: String, edition: Edition) -> bool {
                             | ItemKind::ExternCrate(..)
                             | ItemKind::ForeignMod(..) => return false,
                             // We found a main function ...
-                            ItemKind::Fn(box Fn { sig, body: Some(block), .. }) if item.ident.name == sym::main => {
+                            ItemKind::Fn(box Fn {
+                                sig, body: Some(block), ..
+                            }) if item.ident.name == sym::main => {
                                 let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
                                 let returns_nothing = match &sig.decl.output {
                                     FnRetTy::Default(..) => true,
@@ -763,14 +770,23 @@ fn has_hyphen(s: &str) -> bool {
     if has_underscore(word) || word.contains("::") || is_camel_case(word) {
         let mut applicability = Applicability::MachineApplicable;
 
-        span_lint_and_sugg(
+        span_lint_and_then(
             cx,
             DOC_MARKDOWN,
             span,
             "item in documentation is missing backticks",
-            "try",
-            format!("`{}`", snippet_with_applicability(cx, span, "..", &mut applicability)),
-            applicability,
+            |diag| {
+                let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
+                diag.span_suggestion_with_style(
+                    span,
+                    "try",
+                    format!("`{}`", snippet),
+                    applicability,
+                    // always show the suggestion in a separate line, since the
+                    // inline presentation adds another pair of backticks
+                    SuggestionStyle::ShowAlways,
+                );
+            },
         );
     }
 }
index 6520bb91fafd214d20f8d5b33a38574efe31c207..176092e5b28003da7671dc77cf1fc03bd40004d5 100644 (file)
@@ -31,6 +31,7 @@
     /// # let y = 2;
     /// if x <= y {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DOUBLE_COMPARISONS,
     complexity,
     "unnecessary double comparisons that can be simplified"
index d0d87b6df9a2d2f73d1f0ef83e6c8b02019bf753..e10f740d24a4193d4c141b7c1f1e96a12239353f 100644 (file)
@@ -32,6 +32,7 @@
     /// // Good
     /// foo(0);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DOUBLE_PARENS,
     complexity,
     "Warn on unnecessary double parentheses"
index 0f3dc866afb6e339d8ee8eb47223f59a12f7e7ec..a8f8e3d8a42c0bba557b0dc04fdbb9b2f9730a6b 100644 (file)
@@ -25,6 +25,7 @@
     /// // still locked
     /// operation_that_requires_mutex_to_be_unlocked();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DROP_REF,
     correctness,
     "calls to `std::mem::drop` with a reference instead of an owned value"
@@ -46,6 +47,7 @@
     /// let x = Box::new(1);
     /// std::mem::forget(&x) // Should have been forget(x), x will still be dropped
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FORGET_REF,
     correctness,
     "calls to `std::mem::forget` with a reference instead of an owned value"
@@ -67,6 +69,7 @@
     /// std::mem::drop(x) // A copy of x is passed to the function, leaving the
     ///                   // original unaffected
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DROP_COPY,
     correctness,
     "calls to `std::mem::drop` with a value that implements Copy"
@@ -94,6 +97,7 @@
     /// std::mem::forget(x) // A copy of x is passed to the function, leaving the
     ///                     // original unaffected
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FORGET_COPY,
     correctness,
     "calls to `std::mem::forget` with a value that implements Copy"
index 3774de625213deefd94ba4dc911677748df5d242..3070d105014f561ea5274b09c733af42b1f4bbad 100644 (file)
@@ -33,6 +33,7 @@
     /// let _micros = dur.subsec_micros();
     /// let _millis = dur.subsec_millis();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DURATION_SUBSEC,
     complexity,
     "checks for calculation of subsecond microseconds or milliseconds"
index b64246515f34f79fab343e868120c52060605f1e..92c56c762aad61624155680f632f127a5f1c683d 100644 (file)
@@ -40,6 +40,7 @@
     ///     // We don't care about zero.
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ELSE_IF_WITHOUT_ELSE,
     restriction,
     "`if` expression with an `else if`, but without a final `else` branch"
index 3453c2da2784fa646fd69b6cdadd106727a43ba9..af9e65e636135d8fb186f7390bb89d8047e46230 100644 (file)
@@ -34,6 +34,7 @@
     ///
     /// struct Test(!);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EMPTY_ENUM,
     pedantic,
     "enum with no variants"
index 57fd24bd4f04d81ca9263a905b84b258a100ae59..3d92eb16870e3d88c8d9460507d4b15cb20c4236 100644 (file)
@@ -54,6 +54,7 @@
     /// # let v = 1;
     /// map.entry(k).or_insert(v);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MAP_ENTRY,
     perf,
     "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`"
index a2c3c7a7b49208c07299ce023af744625e935fd9..3b6661c817be7677fe01941e096af0e4f786c1ac 100644 (file)
@@ -28,6 +28,7 @@
     ///     Y = 0,
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ENUM_CLIKE_UNPORTABLE_VARIANT,
     correctness,
     "C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`"
index 404b67c8f29f2817a9e6f58c3fc55d3b588075e4..fc3a35efaf84db0afa05bffedd3e8f92245a75ef 100644 (file)
@@ -34,6 +34,7 @@
     ///     Battenberg,
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ENUM_VARIANT_NAMES,
     style,
     "enums where all variants share a prefix/postfix"
@@ -59,6 +60,7 @@
     ///     struct BlackForest;
     /// }
     /// ```
+    #[clippy::version = "1.33.0"]
     pub MODULE_NAME_REPETITIONS,
     pedantic,
     "type names prefixed/postfixed with their containing module's name"
@@ -89,6 +91,7 @@
     ///     ...
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MODULE_INCEPTION,
     style,
     "modules that have the same name as their parent module"
index 655560afd4250045d8675529fc41bb5abf6f8677..101234605273393b13d7754d8ff28e0e1ee37725 100644 (file)
@@ -1,9 +1,7 @@
 use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::{implements_trait, is_copy};
-use clippy_utils::{
-    ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of, is_in_test_function,
-};
+use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, is_expn_of, is_in_test_function};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind};
@@ -36,6 +34,7 @@
     /// # let b = 4;
     /// assert_eq!(a, a);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EQ_OP,
     correctness,
     "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
@@ -61,6 +60,7 @@
     /// // Good
     /// x == *y
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OP_REF,
     style,
     "taking a reference to satisfy the type constraints on `==`"
@@ -102,7 +102,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
             }
             let macro_with_not_op = |expr_kind: &ExprKind<'_>| {
                 if let ExprKind::Unary(_, expr) = *expr_kind {
-                    in_macro(expr.span)
+                    expr.span.from_expansion()
                 } else {
                     false
                 }
index e8b1d6f6edaaaeb27b1a41daf89a9cd1e0580248..8905cc0de45779408368701d73cb92f5b80196d1 100644 (file)
@@ -32,6 +32,7 @@
     ///     do_thing();
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub EQUATABLE_IF_LET,
     nursery,
     "using pattern matching instead of equality"
index d0944c37cf5f76b42948e55fe2e18070fc6681c4..d49cec26be5f02c6933c72e50227b5c8501b05ad 100644 (file)
@@ -21,6 +21,7 @@
     /// 0 * x;
     /// x & 0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ERASING_OP,
     correctness,
     "using erasing operations, e.g., `x * 0` or `y & 0`"
index 75b1c882c233628ca4e4fd89f769dc1fc25b6e81..bc5d2f6278dee7e9c05fd58ce40fc0b80b7d16af 100644 (file)
@@ -41,6 +41,7 @@ pub struct BoxedLocal {
     /// foo(x);
     /// println!("{}", x);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BOXED_LOCAL,
     perf,
     "using `Box<T>` where unnecessary"
index 9247343b52a5322f8c7ee09eec77de8b655ffcff..5a4b424710440f03b947c4b03cabb337839df59d 100644 (file)
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::higher::VecArgs;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::usage::UsedAfterExprVisitor;
-use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local_id};
+use clippy_utils::usage::local_used_after_expr;
+use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
@@ -39,6 +39,7 @@
     /// ```
     /// where `foo(_)` is a plain function that takes the exact argument type of
     /// `x`.
+    #[clippy::version = "pre 1.29.0"]
     pub REDUNDANT_CLOSURE,
     style,
     "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)"
@@ -60,6 +61,7 @@
     /// ```rust,ignore
     /// Some('a').map(char::to_uppercase);
     /// ```
+    #[clippy::version = "1.35.0"]
     pub REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
     pedantic,
     "redundant closures for method calls"
@@ -118,7 +120,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                             if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
                             if substs.as_closure().kind() == ClosureKind::FnMut;
                             if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
-                                || UsedAfterExprVisitor::is_found(cx, callee);
+                                || path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee));
 
                             then {
                                 // Mutable closure is used after current expr; we cannot consume it.
index 1b56dd4b081775c685de8a295d1ce6c715f02ed3..cdac9f3e6e1776949edb94d07a993b42f0d05c8a 100644 (file)
@@ -40,6 +40,7 @@
     /// };
     /// let a = tmp + x;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EVAL_ORDER_DEPENDENCE,
     suspicious,
     "whether a variable read occurs before a write depends on sub-expression evaluation order"
@@ -67,6 +68,7 @@
     /// let x = (a, b, c, panic!());
     /// // can simply be replaced by `panic!()`
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DIVERGING_SUB_EXPRESSION,
     complexity,
     "whether an expression contains a diverging sub expression"
index 09b6e20083889e863ac884636ad2c3b20f08db50..245a4fc12fd4c04c6e305e83103fcc43bbbc0af9 100644 (file)
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::in_macro;
 use rustc_ast::ast::{AssocItemKind, Extern, Fn, FnSig, Impl, Item, ItemKind, Trait, Ty, TyKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -38,6 +37,7 @@
     ///     Finished,
     /// }
     /// ```
+    #[clippy::version = "1.43.0"]
     pub STRUCT_EXCESSIVE_BOOLS,
     pedantic,
     "using too many bools in a struct"
@@ -76,6 +76,7 @@
     ///
     /// fn f(shape: Shape, temperature: Temperature) { ... }
     /// ```
+    #[clippy::version = "1.43.0"]
     pub FN_PARAMS_EXCESSIVE_BOOLS,
     pedantic,
     "using too many bools in function parameters"
@@ -135,7 +136,7 @@ fn is_bool_ty(ty: &Ty) -> bool {
 
 impl EarlyLintPass for ExcessiveBools {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-        if in_macro(item.span) {
+        if item.span.from_expansion() {
             return;
         }
         match &item.kind {
index bb4684ce38b3d7eba4b74988aa8fe5cad9ddf05f..b0f50b5c144bbe45a6487cee425aa3501258169f 100644 (file)
@@ -31,6 +31,7 @@
     ///     Baz
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub EXHAUSTIVE_ENUMS,
     restriction,
     "detects exported enums that have not been marked #[non_exhaustive]"
@@ -60,6 +61,7 @@
     ///     baz: String,
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub EXHAUSTIVE_STRUCTS,
     restriction,
     "detects exported structs that have not been marked #[non_exhaustive]"
index 9cd5b2d9f4439e20fadd7785f102a53458c3aba5..d64cc61916c5eb4cf0bf00314253cc8b229d4b97 100644 (file)
@@ -18,6 +18,7 @@
     /// ```ignore
     /// std::process::exit(0)
     /// ```
+    #[clippy::version = "1.41.0"]
     pub EXIT,
     restriction,
     "`std::process::exit` is called, terminating the program"
index 4f46ef906f4098517137997f64068f5ccf9a13ba..6b327b9ce1720aeb1fbde29b499fb174f8a393e5 100644 (file)
@@ -23,6 +23,7 @@
     /// // this would be clearer as `eprintln!("foo: {:?}", bar);`
     /// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPLICIT_WRITE,
     complexity,
     "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
index 70337f5bbeb04039250630821b04d7e1a2b1bfd0..05d300058cf4f270ca6e9713c40deb1bbe7b1e8a 100644 (file)
@@ -44,6 +44,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FALLIBLE_IMPL_FROM,
     nursery,
     "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
index f534327f7a0cda0bccafcbdca12c0205e5625956..dc6bef52ddd9fb3256168e3cec80fd9a2bfb0af1 100644 (file)
@@ -31,6 +31,7 @@
     /// ghi = []
     /// ```
     ///
+    #[clippy::version = "1.57.0"]
     pub REDUNDANT_FEATURE_NAMES,
     cargo,
     "usage of a redundant feature name"
@@ -60,6 +61,7 @@
     /// def = []
     ///
     /// ```
+    #[clippy::version = "1.57.0"]
     pub NEGATIVE_FEATURE_NAMES,
     cargo,
     "usage of a negative feature name"
index c33d80b8e8ef9aee958b93d3819a78829d3775d9..ca8886228de264a042c24c89e2f9688d4c15bb9f 100644 (file)
@@ -37,6 +37,7 @@
     ///     (a - b).abs() < f32::EPSILON
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub FLOAT_EQUALITY_WITHOUT_ABS,
     suspicious,
     "float equality check without `.abs()`"
index 1e8a5bd7d34453be68f0b322047f601ed1f789d6..d30dede833cbf50104651c2112b361d6ffbf5157 100644 (file)
@@ -27,6 +27,7 @@
     /// let v: f64 = 0.123_456_789_9;
     /// println!("{}", v); //  0.123_456_789_9
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXCESSIVE_PRECISION,
     style,
     "excessive precision for float literal"
@@ -50,6 +51,7 @@
     /// let _: f32 = 16_777_216.0;
     /// let _: f64 = 16_777_217.0;
     /// ```
+    #[clippy::version = "1.43.0"]
     pub LOSSY_FLOAT_LITERAL,
     restriction,
     "lossy whole number float literals"
index eda611117babf9c5561b09dd4b6bea8d94164b10..3df511ea8e780e20496d0636a3af3435b5fa001e 100644 (file)
@@ -4,7 +4,7 @@
 };
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher;
-use clippy_utils::{eq_expr_value, get_parent_expr, numeric_literal, sugg};
+use clippy_utils::{eq_expr_value, get_parent_expr, in_constant, numeric_literal, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
@@ -43,6 +43,7 @@
     /// let _ = a.ln_1p();
     /// let _ = a.exp_m1();
     /// ```
+    #[clippy::version = "1.43.0"]
     pub IMPRECISE_FLOPS,
     nursery,
     "usage of imprecise floating point operations"
     /// let _ = a.abs();
     /// let _ = -a.abs();
     /// ```
+    #[clippy::version = "1.43.0"]
     pub SUBOPTIMAL_FLOPS,
     nursery,
     "usage of sub-optimal floating point operations"
@@ -685,6 +687,11 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
 
 impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        // All of these operations are currently not const.
+        if in_constant(cx, expr.hir_id) {
+            return;
+        }
+
         if let ExprKind::MethodCall(path, _, args, _) = &expr.kind {
             let recv_ty = cx.typeck_results().expr_ty(&args[0]);
 
index 7169ac9ad6c5a50ce98062c8d1dcdaa465adea46..3f043e5f2f1c55be2ec3b1833297bfb76e8d547b 100644 (file)
@@ -33,6 +33,7 @@
     /// // Good
     /// foo.to_owned();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_FORMAT,
     complexity,
     "useless use of `format!`"
index 8b27442aa94656453060d0f80f558ae972b7a69e..f0e1a67dcddb93d852d21eeae72b812be8280dbe 100644 (file)
@@ -10,7 +10,7 @@
 use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 use rustc_middle::ty::Ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{sym, BytePos, ExpnData, ExpnKind, Span, Symbol};
+use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -31,6 +31,7 @@
     /// # use std::panic::Location;
     /// println!("error: something failed at {}", Location::caller());
     /// ```
+    #[clippy::version = "1.58.0"]
     pub FORMAT_IN_FORMAT_ARGS,
     perf,
     "`format!` used in a macro that does formatting"
@@ -56,6 +57,7 @@
     /// # use std::panic::Location;
     /// println!("error: something failed at {}", Location::caller());
     /// ```
+    #[clippy::version = "1.58.0"]
     pub TO_STRING_IN_FORMAT_ARGS,
     perf,
     "`to_string` applied to a type that implements `Display` in format args"
@@ -128,7 +130,7 @@ fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symb
             span_lint_and_then(
                 cx,
                 FORMAT_IN_FORMAT_ARGS,
-                trim_semicolon(cx, call_site),
+                call_site,
                 &format!("`format!` in `{}!` args", name),
                 |diag| {
                     diag.help(&format!(
@@ -192,13 +194,6 @@ fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool {
         .any(|(j, arg)| i != j && std::ptr::eq(value, arg.value))
 }
 
-fn trim_semicolon(cx: &LateContext<'_>, span: Span) -> Span {
-    snippet_opt(cx, span).map_or(span, |snippet| {
-        let snippet = snippet.trim_end_matches(';');
-        span.with_hi(span.lo() + BytePos(u32::try_from(snippet.len()).unwrap()))
-    })
-}
-
 fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
 where
     I: Iterator<Item = &'tcx Adjustment<'tcx>>,
index b4f186525c562ea4cd33c4dcdcf9bddd08ed1d92..3e85c8a9c8071a91bc65f45dbc847207e743e1ff 100644 (file)
@@ -21,6 +21,7 @@
     /// ```rust,ignore
     /// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
     suspicious,
     "suspicious formatting of `*=`, `-=` or `!=`"
@@ -43,6 +44,7 @@
     /// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub SUSPICIOUS_UNARY_OP_FORMATTING,
     suspicious,
     "suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
@@ -79,6 +81,7 @@
     /// if bar { // this is the `else` block of the previous `if`, but should it be?
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SUSPICIOUS_ELSE_FORMATTING,
     suspicious,
     "suspicious formatting of `else`"
     ///     -4, -5, -6
     /// ];
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub POSSIBLE_MISSING_COMMA,
     correctness,
     "possible missing comma in array"
index 347c6eb12cbcecb1715760ac65f77f7c7adff93f..866ff216f84a21df3dc04355760f8d8e4a11e40b 100644 (file)
@@ -34,6 +34,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub FROM_OVER_INTO,
     style,
     "Warns on implementations of `Into<..>` to use `From<..>`"
index 98ce3db025caa015b4b42e1f7556d3570b0348d6..73e800073b03fe6c0f0e6d93ceef01a834f0dd61 100644 (file)
@@ -35,6 +35,7 @@
     /// let input: &str = get_input();
     /// let num: u16 = input.parse()?;
     /// ```
+    #[clippy::version = "1.52.0"]
     pub FROM_STR_RADIX_10,
     style,
     "from_str_radix with radix 10"
index d7c5ec9ba355bdf4bcc4118bc518554718afd4cf..ad031cbc09d4d0f631b4fcf87df345aa97924985 100644 (file)
@@ -26,6 +26,7 @@
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TOO_MANY_ARGUMENTS,
     complexity,
     "functions with too many arguments"
@@ -49,6 +50,7 @@
     ///     println!("");
     /// }
     /// ```
+    #[clippy::version = "1.34.0"]
     pub TOO_MANY_LINES,
     pedantic,
     "functions with too many lines"
@@ -84,6 +86,7 @@
     ///     println!("{}", unsafe { *x });
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NOT_UNSAFE_PTR_ARG_DEREF,
     correctness,
     "public functions dereferencing raw pointer arguments but not marked `unsafe`"
     /// #[must_use]
     /// fn useless() { }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub MUST_USE_UNIT,
     style,
     "`#[must_use]` attribute on a unit-returning function / method"
     ///     unimplemented!();
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub DOUBLE_MUST_USE,
     style,
     "`#[must_use]` attribute on a `#[must_use]`-returning function / method"
     /// // this could be annotated with `#[must_use]`.
     /// fn id<T>(t: T) -> T { t }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub MUST_USE_CANDIDATE,
     pedantic,
     "function or method that could take a `#[must_use]` attribute"
     ///
     /// Note that there are crates that simplify creating the error type, e.g.
     /// [`thiserror`](https://docs.rs/thiserror).
+    #[clippy::version = "1.49.0"]
     pub RESULT_UNIT_ERR,
     style,
     "public function returning `Result` with an `Err` type of `()`"
index 008ef661b55f20a31c4e25e1e24e3a0de4b7a94a..65efbbab41a460f0ab4f9b2c84a400cbe22efeee 100644 (file)
@@ -56,8 +56,8 @@ pub(super) fn check_fn(
                     continue;
                 }
             } else {
-                let multi_idx = line.find("/*").unwrap_or_else(|| line.len());
-                let single_idx = line.find("//").unwrap_or_else(|| line.len());
+                let multi_idx = line.find("/*").unwrap_or(line.len());
+                let single_idx = line.find("//").unwrap_or(line.len());
                 code_in_line |= multi_idx > 0 && single_idx > 0;
                 // Implies multi_idx is below line.len()
                 if multi_idx < single_idx {
index 6b2ac985555dc59fc7014b3c0afeb4267ffd9420..fefdcfed42f5465fd1235a96a46596368e0b6194 100644 (file)
@@ -41,6 +41,7 @@
     /// ```rust
     /// async fn is_send(bytes: std::sync::Arc<[u8]>) {}
     /// ```
+    #[clippy::version = "1.44.0"]
     pub FUTURE_NOT_SEND,
     nursery,
     "public Futures must be Send"
index f3929b0f1e617fd0ad2740c889d81afc817d8da7..edca701869e0fc2eb89dd14574a9700957f414df 100644 (file)
@@ -39,6 +39,7 @@
     /// let x = vec![2, 3, 5];
     /// let last_element = x.last();
     /// ```
+    #[clippy::version = "1.37.0"]
     pub GET_LAST_WITH_LEN,
     complexity,
     "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
index 414f465c49414088113b36ee2eb09f45c72b9964..b4e7bbc767135474d1c33d5b3b601e4548a89ad0 100644 (file)
@@ -22,6 +22,7 @@
     /// # let x = 1;
     /// x / 1 + 0 * 1 - 0 | 0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IDENTITY_OP,
     complexity,
     "using identity operations, e.g., `x + 0` or `y / 1`"
index a4118bf54b68313617425862dbe87ed5aae80826..e20741d2407e63b21a09ff0b4d40b299dbedcd77 100644 (file)
@@ -36,6 +36,7 @@
     ///     use_locked(locked);
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub IF_LET_MUTEX,
     correctness,
     "locking a `Mutex` in an `if let` block can cause deadlocks"
index ac938156237bf8f090feb281558f6301b685e6b0..3d59b783337a441b3ca1151f19667fe0174b41b4 100644 (file)
@@ -39,6 +39,7 @@
     ///     a()
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IF_NOT_ELSE,
     pedantic,
     "`if` branches that could be swapped so no negation operation is necessary on the condition"
index a2dac57454f2d3b7430d4127c70dec75b1952e64..30d222bd7d27de9643047c51f282d05357aadba1 100644 (file)
@@ -1,9 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs};
+use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_semver::RustcVersion;
@@ -36,6 +36,7 @@
     ///     42
     /// });
     /// ```
+    #[clippy::version = "1.53.0"]
     pub IF_THEN_SOME_ELSE_NONE,
     restriction,
     "Finds if-else that could be written using `bool::then`"
@@ -81,6 +82,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
             if let Some(els_expr) = els_block.expr;
             if let ExprKind::Path(ref qpath) = els_expr.kind;
             if is_lang_ctor(cx, qpath, OptionNone);
+            if !stmts_contains_early_return(then_block.stmts);
             then {
                 let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
                 let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
@@ -113,3 +115,11 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
 
     extract_msrv_attr!(LateContext);
 }
+
+fn stmts_contains_early_return(stmts: &[Stmt<'_>]) -> bool {
+    stmts.iter().any(|stmt| {
+        let Stmt { kind: StmtKind::Semi(e), .. } = stmt else { return false };
+
+        contains_return(e)
+    })
+}
index 81eb51e6f7cee304c034b367544f32eaa0268846..6358228dd47f0e421cc06a7f1306655db868234f 100644 (file)
@@ -54,6 +54,7 @@
     ///
     /// pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IMPLICIT_HASHER,
     pedantic,
     "missing generalization over different hashers"
index fa7b5302cb13135a9e30986c40ff3b6205bfe27a..07caeada80d003fa96f48474a741c22bacf121d0 100644 (file)
@@ -2,10 +2,10 @@
     diagnostics::span_lint_and_sugg,
     get_async_fn_body, is_async_fn,
     source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
-    visitors::visit_break_exprs,
+    visitors::expr_visitor_no_bodies,
 };
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::FnKind;
+use rustc_hir::intravisit::{FnKind, Visitor};
 use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -35,6 +35,7 @@
     ///     return x;
     /// }
     /// ```
+    #[clippy::version = "1.33.0"]
     pub IMPLICIT_RETURN,
     restriction,
     "use a return statement like `return expr` instead of an expression"
@@ -144,20 +145,24 @@ fn lint_implicit_returns(
 
         ExprKind::Loop(block, ..) => {
             let mut add_return = false;
-            visit_break_exprs(block, |break_expr, dest, sub_expr| {
-                if dest.target_id.ok() == Some(expr.hir_id) {
-                    if call_site_span.is_none() && break_expr.span.ctxt() == ctxt {
-                        // At this point sub_expr can be `None` in async functions which either diverge, or return the
-                        // unit type.
-                        if let Some(sub_expr) = sub_expr {
-                            lint_break(cx, break_expr.span, sub_expr.span);
+            expr_visitor_no_bodies(|e| {
+                if let ExprKind::Break(dest, sub_expr) = e.kind {
+                    if dest.target_id.ok() == Some(expr.hir_id) {
+                        if call_site_span.is_none() && e.span.ctxt() == ctxt {
+                            // At this point sub_expr can be `None` in async functions which either diverge, or return
+                            // the unit type.
+                            if let Some(sub_expr) = sub_expr {
+                                lint_break(cx, e.span, sub_expr.span);
+                            }
+                        } else {
+                            // the break expression is from a macro call, add a return to the loop
+                            add_return = true;
                         }
-                    } else {
-                        // the break expression is from a macro call, add a return to the loop
-                        add_return = true;
                     }
                 }
-            });
+                true
+            })
+            .visit_block(block);
             if add_return {
                 #[allow(clippy::option_if_let_else)]
                 if let Some(span) = call_site_span {
index a4f60ded3a6e0ebcaadab8e2b92ab712211d5159..4088c54623d36a3dc4187589ada0bf02e4345053 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher;
-use clippy_utils::{in_macro, SpanlessEq};
+use clippy_utils::SpanlessEq;
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -30,6 +30,7 @@
     /// // Good
     /// i = i.saturating_sub(1);
     /// ```
+    #[clippy::version = "1.44.0"]
     pub IMPLICIT_SATURATING_SUB,
     pedantic,
     "Perform saturating subtraction instead of implicitly checking lower bound of data type"
@@ -39,7 +40,7 @@
 
 impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if in_macro(expr.span) {
+        if expr.span.from_expansion() {
             return;
         }
         if_chain! {
index 1f8240a1f636a269a4e9918ec59d98040e1c4bb0..1debdef9d86c7b077594bef9115ec3fbf70d8f9b 100644 (file)
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use clippy_utils::source::snippet;
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashMap;
@@ -56,6 +55,7 @@
     /// # let y = 2;
     /// Foo { x, y };
     /// ```
+    #[clippy::version = "1.52.0"]
     pub INCONSISTENT_STRUCT_CONSTRUCTOR,
     pedantic,
     "the order of the field init shorthand is inconsistent with the order in the struct definition"
@@ -66,7 +66,7 @@
 impl LateLintPass<'_> for InconsistentStructConstructor {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if_chain! {
-            if !in_macro(expr.span);
+            if !expr.span.from_expansion();
             if let ExprKind::Struct(qpath, fields, base) = expr.kind;
             let ty = cx.typeck_results().expr_ty(expr);
             if let Some(adt_def) = ty.ty_adt_def();
diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
new file mode 100644 (file)
index 0000000..69f1c90
--- /dev/null
@@ -0,0 +1,276 @@
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::higher::IfLet;
+use clippy_utils::ty::is_copy;
+use clippy_utils::{is_expn_of, is_lint_allowed, meets_msrv, msrvs, path_to_local};
+use if_chain::if_chain;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::hir::map::Map;
+use rustc_middle::ty;
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{symbol::Ident, Span};
+use std::convert::TryInto;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// The lint checks for slice bindings in patterns that are only used to
+    /// access individual slice values.
+    ///
+    /// ### Why is this bad?
+    /// Accessing slice values using indices can lead to panics. Using refutable
+    /// patterns can avoid these. Binding to individual values also improves the
+    /// readability as they can be named.
+    ///
+    /// ### Limitations
+    /// This lint currently only checks for immutable access inside `if let`
+    /// patterns.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    ///
+    /// if let Some(slice) = slice {
+    ///     println!("{}", slice[0]);
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    ///
+    /// if let Some(&[first, ..]) = slice {
+    ///     println!("{}", first);
+    /// }
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub INDEX_REFUTABLE_SLICE,
+    nursery,
+    "avoid indexing on slices which could be destructed"
+}
+
+#[derive(Copy, Clone)]
+pub struct IndexRefutableSlice {
+    max_suggested_slice: u64,
+    msrv: Option<RustcVersion>,
+}
+
+impl IndexRefutableSlice {
+    pub fn new(max_suggested_slice_pattern_length: u64, msrv: Option<RustcVersion>) -> Self {
+        Self {
+            max_suggested_slice: max_suggested_slice_pattern_length,
+            msrv,
+        }
+    }
+}
+
+impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]);
+
+impl LateLintPass<'_> for IndexRefutableSlice {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+        if_chain! {
+            if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some();
+            if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr);
+            if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id);
+            if meets_msrv(self.msrv.as_ref(), &msrvs::SLICE_PATTERNS);
+
+            let found_slices = find_slice_values(cx, let_pat);
+            if !found_slices.is_empty();
+            let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then);
+            if !filtered_slices.is_empty();
+            then {
+                for slice in filtered_slices.values() {
+                    lint_slice(cx, slice);
+                }
+            }
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
+
+fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxHashMap<hir::HirId, SliceLintInformation> {
+    let mut removed_pat: FxHashSet<hir::HirId> = FxHashSet::default();
+    let mut slices: FxHashMap<hir::HirId, SliceLintInformation> = FxHashMap::default();
+    pat.walk_always(|pat| {
+        if let hir::PatKind::Binding(binding, value_hir_id, ident, sub_pat) = pat.kind {
+            // We'll just ignore mut and ref mut for simplicity sake right now
+            if let hir::BindingAnnotation::Mutable | hir::BindingAnnotation::RefMut = binding {
+                return;
+            }
+
+            // This block catches bindings with sub patterns. It would be hard to build a correct suggestion
+            // for them and it's likely that the user knows what they are doing in such a case.
+            if removed_pat.contains(&value_hir_id) {
+                return;
+            }
+            if sub_pat.is_some() {
+                removed_pat.insert(value_hir_id);
+                slices.remove(&value_hir_id);
+                return;
+            }
+
+            let bound_ty = cx.typeck_results().node_type(pat.hir_id);
+            if let ty::Slice(inner_ty) | ty::Array(inner_ty, _) = bound_ty.peel_refs().kind() {
+                // The values need to use the `ref` keyword if they can't be copied.
+                // This will need to be adjusted if the lint want to support multable access in the future
+                let src_is_ref = bound_ty.is_ref() && binding != hir::BindingAnnotation::Ref;
+                let needs_ref = !(src_is_ref || is_copy(cx, inner_ty));
+
+                let slice_info = slices
+                    .entry(value_hir_id)
+                    .or_insert_with(|| SliceLintInformation::new(ident, needs_ref));
+                slice_info.pattern_spans.push(pat.span);
+            }
+        }
+    });
+
+    slices
+}
+
+fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) {
+    let used_indices = slice
+        .index_use
+        .iter()
+        .map(|(index, _)| *index)
+        .collect::<FxHashSet<_>>();
+
+    let value_name = |index| format!("{}_{}", slice.ident.name, index);
+
+    if let Some(max_index) = used_indices.iter().max() {
+        let opt_ref = if slice.needs_ref { "ref " } else { "" };
+        let pat_sugg_idents = (0..=*max_index)
+            .map(|index| {
+                if used_indices.contains(&index) {
+                    format!("{}{}", opt_ref, value_name(index))
+                } else {
+                    "_".to_string()
+                }
+            })
+            .collect::<Vec<_>>();
+        let pat_sugg = format!("[{}, ..]", pat_sugg_idents.join(", "));
+
+        span_lint_and_then(
+            cx,
+            INDEX_REFUTABLE_SLICE,
+            slice.ident.span,
+            "this binding can be a slice pattern to avoid indexing",
+            |diag| {
+                diag.multipart_suggestion(
+                    "try using a slice pattern here",
+                    slice
+                        .pattern_spans
+                        .iter()
+                        .map(|span| (*span, pat_sugg.clone()))
+                        .collect(),
+                    Applicability::MaybeIncorrect,
+                );
+
+                diag.multipart_suggestion(
+                    "and replace the index expressions here",
+                    slice
+                        .index_use
+                        .iter()
+                        .map(|(index, span)| (*span, value_name(*index)))
+                        .collect(),
+                    Applicability::MaybeIncorrect,
+                );
+
+                // The lint message doesn't contain a warning about the removed index expression,
+                // since `filter_lintable_slices` will only return slices where all access indices
+                // are known at compile time. Therefore, they can be removed without side effects.
+            },
+        );
+    }
+}
+
+#[derive(Debug)]
+struct SliceLintInformation {
+    ident: Ident,
+    needs_ref: bool,
+    pattern_spans: Vec<Span>,
+    index_use: Vec<(u64, Span)>,
+}
+
+impl SliceLintInformation {
+    fn new(ident: Ident, needs_ref: bool) -> Self {
+        Self {
+            ident,
+            needs_ref,
+            pattern_spans: Vec::new(),
+            index_use: Vec::new(),
+        }
+    }
+}
+
+fn filter_lintable_slices<'a, 'tcx>(
+    cx: &'a LateContext<'tcx>,
+    slice_lint_info: FxHashMap<hir::HirId, SliceLintInformation>,
+    max_suggested_slice: u64,
+    scope: &'tcx hir::Expr<'tcx>,
+) -> FxHashMap<hir::HirId, SliceLintInformation> {
+    let mut visitor = SliceIndexLintingVisitor {
+        cx,
+        slice_lint_info,
+        max_suggested_slice,
+    };
+
+    intravisit::walk_expr(&mut visitor, scope);
+
+    visitor.slice_lint_info
+}
+
+struct SliceIndexLintingVisitor<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    slice_lint_info: FxHashMap<hir::HirId, SliceLintInformation>,
+    max_suggested_slice: u64,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> {
+    type Map = Map<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+    }
+
+    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+        if let Some(local_id) = path_to_local(expr) {
+            let Self {
+                cx,
+                ref mut slice_lint_info,
+                max_suggested_slice,
+            } = *self;
+
+            if_chain! {
+                // Check if this is even a local we're interested in
+                if let Some(use_info) = slice_lint_info.get_mut(&local_id);
+
+                let map = cx.tcx.hir();
+
+                // Checking for slice indexing
+                let parent_id = map.get_parent_node(expr.hir_id);
+                if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id);
+                if let hir::ExprKind::Index(_, index_expr) = parent_expr.kind;
+                if let Some((Constant::Int(index_value), _)) = constant(cx, cx.typeck_results(), index_expr);
+                if let Ok(index_value) = index_value.try_into();
+                if index_value < max_suggested_slice;
+
+                // Make sure that this slice index is read only
+                let maybe_addrof_id = map.get_parent_node(parent_id);
+                if let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id);
+                if let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind;
+                then {
+                    use_info.index_use.push((index_value, map.span(parent_expr.hir_id)));
+                    return;
+                }
+            }
+
+            // The slice was used for something other than indexing
+            self.slice_lint_info.remove(&local_id);
+        }
+        intravisit::walk_expr(self, expr);
+    }
+}
index f52f090d3872e11ab873ada16b9a0621620d299e..9ead4bb27a5881eb3ab43d6def4481f658999b44 100644 (file)
@@ -33,6 +33,7 @@
     /// x[0];
     /// x[3];
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OUT_OF_BOUNDS_INDEXING,
     correctness,
     "out of bounds constant indexing"
@@ -85,6 +86,7 @@
     /// y.get(10..);
     /// y.get(..100);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INDEXING_SLICING,
     restriction,
     "indexing/slicing usage"
index 68c1fa35fcc4eb919de7512f654cede557cd0877..c7db47a552b2cef43093f7af8e4094b3b91025dc 100644 (file)
@@ -20,6 +20,7 @@
     ///
     /// iter::repeat(1_u8).collect::<Vec<_>>();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INFINITE_ITER,
     correctness,
     "infinite iteration"
@@ -42,6 +43,7 @@
     /// let infinite_iter = 0..;
     /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MAYBE_INFINITE_ITER,
     pedantic,
     "possible infinite iteration"
index 0d23bec27a3a753e7789ecde2e20db8f068a13d9..254d3f8a4d0f901a672585236c66e19819d963a5 100644 (file)
@@ -1,7 +1,7 @@
 //! lint on inherent implementations
 
 use clippy_utils::diagnostics::span_lint_and_note;
-use clippy_utils::{in_macro, is_lint_allowed};
+use clippy_utils::is_lint_allowed;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{def_id::LocalDefId, Item, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
@@ -36,6 +36,7 @@
     ///     fn other() {}
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MULTIPLE_INHERENT_IMPL,
     restriction,
     "Multiple inherent impl that could be grouped"
@@ -123,8 +124,10 @@ fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option<Span> {
         ..
     }) = cx.tcx.hir().get(id)
     {
-        (!in_macro(span) && impl_item.generics.params.is_empty() && !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id))
-            .then(|| span)
+        (!span.from_expansion()
+            && impl_item.generics.params.is_empty()
+            && !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id))
+        .then(|| span)
     } else {
         None
     }
index 61dd0eb4af7ed37d7239f2b334bbed4712a7edf8..60d234cd6f08f19fcb904f3b68df5a5ccab418b1 100644 (file)
@@ -41,6 +41,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.38.0"]
     pub INHERENT_TO_STRING,
     style,
     "type implements inherent method `to_string()`, but should instead implement the `Display` trait"
@@ -88,6 +89,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.38.0"]
     pub INHERENT_TO_STRING_SHADOW_DISPLAY,
     correctness,
     "type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait"
index 3e3df903f17cf27156b1bdf2a9d8ce46c39ddc74..df69d3dcc51603fa18e14d8177667c359f3c179a 100644 (file)
@@ -24,6 +24,7 @@
     ///     fn name(&self) -> &'static str;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INLINE_FN_WITHOUT_BODY,
     correctness,
     "use of `#[inline]` on trait methods without bodies"
index 6850e0c34767cb0864a002b5ce498c3cc157c5a8..3716d36ad88168a2fb7eb84f1a02484981c3d341 100644 (file)
@@ -28,6 +28,7 @@
     /// # let y = 1;
     /// if x > y {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INT_PLUS_ONE,
     complexity,
     "instead of using `x >= y + 1`, use `x > y`"
index c962e814fa5c2eb33af2e3d110857afe409f3bea..fa78620567880e64488c3d613240e07313907339 100644 (file)
@@ -23,6 +23,7 @@
     /// let x = 3f32 / 2f32;
     /// println!("{}", x);
     /// ```
+    #[clippy::version = "1.37.0"]
     pub INTEGER_DIVISION,
     restriction,
     "integer division may cause loss of precision"
index 82438d85c7a3a353128134b0736e587c4dc9d5f1..36e03e50a8e4f4c2c950bb297f88b8d374524f6d 100644 (file)
@@ -30,6 +30,7 @@
     /// let x: u8 = 1;
     /// (x as u32) > 300;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INVALID_UPCAST_COMPARISONS,
     pedantic,
     "a comparison involving an upcast which is always true or false"
index 3736d237642fb61d30b3ddadbb8148fabd2d3ca5..b118d3c8b8727242427c85d4383eb381458e3ddb 100644 (file)
@@ -45,6 +45,7 @@
     ///     foo(); // prints "foo"
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITEMS_AFTER_STATEMENTS,
     pedantic,
     "blocks where an item comes after a statement"
index 6c779348ca28a4f5e6484392c033350f31188747..968bbc524b2514080059f90daa9b04bb3a61d0b9 100644 (file)
@@ -32,6 +32,7 @@
     ///    }
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub ITER_NOT_RETURNING_ITERATOR,
     pedantic,
     "methods named `iter` or `iter_mut` that do not return an `Iterator`"
index fe6814e35d0ca1da4475f6428eb0c3fcd8186ec9..80260e4cd8315f3c42738bdd19cd0afccd2b1c1c 100644 (file)
@@ -27,6 +27,7 @@
     /// // Good
     /// pub static a = [0u32; 1_000_000];
     /// ```
+    #[clippy::version = "1.44.0"]
     pub LARGE_CONST_ARRAYS,
     perf,
     "large non-scalar const array may cause performance overhead"
index 392166237be50eb8fd785afd57902d58c0874c24..0191713f60d3f599f773303b61dfa3331c1170ae 100644 (file)
@@ -40,6 +40,7 @@
     ///     B(Box<[i32; 8000]>),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LARGE_ENUM_VARIANT,
     perf,
     "large size difference between variants on an enum"
index bbb6c1f902ce077e7fcfc6f168021f71fb356cde..1cc2c28c04ad4dfb0c351091ec550ed753cb0826 100644 (file)
@@ -19,6 +19,7 @@
     /// ```rust,ignore
     /// let a = [0u32; 1_000_000];
     /// ```
+    #[clippy::version = "1.41.0"]
     pub LARGE_STACK_ARRAYS,
     pedantic,
     "allocating large arrays on stack may cause stack overflow"
index f336fb9d42f88be86071b100ee85a83e0f6c44f7..09cd0d22d8b0fb3eb45da2f41aa21f28eb3e85c2 100644 (file)
@@ -46,6 +46,7 @@
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LEN_ZERO,
     style,
     "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
@@ -71,6 +72,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LEN_WITHOUT_IS_EMPTY,
     style,
     "traits or impls with a public `len` method but no corresponding `is_empty` method"
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub COMPARISON_TO_EMPTY,
     style,
     "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
index 7f2c7b707f0b1290968371fbb65e45b4dd32b86d..db09d00d7303f3e80cdd876be081a7ac664c2b29 100644 (file)
@@ -48,6 +48,7 @@
     ///     None
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_LET_IF_SEQ,
     nursery,
     "unidiomatic `let mut` declaration followed by initialization in `if`"
index 9efd7aba7e83bccf09af6aea4f86da86285eeeda..d03276f7f98ba70912584d40a3256c88c0d86d58 100644 (file)
@@ -26,6 +26,7 @@
     /// // is_ok() is marked #[must_use]
     /// let _ = f().is_ok();
     /// ```
+    #[clippy::version = "1.42.0"]
     pub LET_UNDERSCORE_MUST_USE,
     restriction,
     "non-binding let on a `#[must_use]` expression"
@@ -33,7 +34,8 @@
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for `let _ = sync_lock`
+    /// Checks for `let _ = sync_lock`.
+    /// This supports `mutex` and `rwlock` in `std::sync` and `parking_lot`.
     ///
     /// ### Why is this bad?
     /// This statement immediately drops the lock instead of
@@ -53,6 +55,7 @@
     /// ```rust,ignore
     /// let _lock = mutex.lock();
     /// ```
+    #[clippy::version = "1.43.0"]
     pub LET_UNDERSCORE_LOCK,
     correctness,
     "non-binding let on a synchronization lock"
@@ -94,6 +97,7 @@
     ///     // dropped at end of scope
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub LET_UNDERSCORE_DROP,
     pedantic,
     "non-binding let on a type that implements `Drop`"
 
 declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]);
 
-const SYNC_GUARD_PATHS: [&[&str]; 3] = [
+const SYNC_GUARD_PATHS: [&[&str]; 5] = [
     &paths::MUTEX_GUARD,
     &paths::RWLOCK_READ_GUARD,
     &paths::RWLOCK_WRITE_GUARD,
+    &paths::PARKING_LOT_RAWMUTEX,
+    &paths::PARKING_LOT_RAWRWLOCK,
 ];
 
 impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
index 15edb79d36c24d613653e870f38dc66ff94ca3e3..b7b5f059de6fc961f7820bfcfc2bae8ae18f2eb3 100644 (file)
@@ -33,6 +33,7 @@
     LintId::of(copies::IFS_SAME_COND),
     LintId::of(copies::IF_SAME_THEN_ELSE),
     LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+    LintId::of(dereference::NEEDLESS_BORROW),
     LintId::of(derivable_impls::DERIVABLE_IMPLS),
     LintId::of(derive::DERIVE_HASH_XOR_EQ),
     LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
     LintId::of(methods::MANUAL_SPLIT_ONCE),
     LintId::of(methods::MANUAL_STR_REPEAT),
     LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
+    LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_IDENTITY),
+    LintId::of(methods::NEEDLESS_SPLITN),
     LintId::of(methods::NEW_RET_NO_SELF),
     LintId::of(methods::OK_EXPECT),
     LintId::of(methods::OPTION_AS_REF_DEREF),
     LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
     LintId::of(needless_bool::BOOL_COMPARISON),
     LintId::of(needless_bool::NEEDLESS_BOOL),
-    LintId::of(needless_borrow::NEEDLESS_BORROW),
     LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
+    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
     LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
     LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
     LintId::of(needless_update::NEEDLESS_UPDATE),
     LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
     LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
     LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
+    LintId::of(octal_escapes::OCTAL_ESCAPES),
     LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
     LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
     LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
index c51341bdf0c233bbcd11a18a7759994acc83d8bc..a21ddf73a115e6a22a54dbfc2867abdb43cf0228 100644 (file)
@@ -41,7 +41,9 @@
     LintId::of(methods::MANUAL_FILTER_MAP),
     LintId::of(methods::MANUAL_FIND_MAP),
     LintId::of(methods::MANUAL_SPLIT_ONCE),
+    LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_IDENTITY),
+    LintId::of(methods::NEEDLESS_SPLITN),
     LintId::of(methods::OPTION_AS_REF_DEREF),
     LintId::of(methods::OPTION_FILTER_MAP),
     LintId::of(methods::SEARCH_IS_SOME),
index c8c1e0262abaea8d62c774e6cad0b8764ab1c5b5..7d4c7d2adb5b9e8063e5bdc2307ff8f33d23f42c 100644 (file)
@@ -9,9 +9,11 @@
     LintId::of(utils::internal_lints::DEFAULT_LINT),
     LintId::of(utils::internal_lints::IF_CHAIN_STYLE),
     LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL),
+    LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE),
     LintId::of(utils::internal_lints::INVALID_PATHS),
     LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS),
     LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
+    LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE),
     LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA),
     LintId::of(utils::internal_lints::PRODUCE_ICE),
     LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR),
index 2cb86418e3cb5df2cc946ba79a8095fe48de7be0..bb159e50373c22d156db89368fcbcb88b5607013 100644 (file)
     #[cfg(feature = "internal-lints")]
     utils::internal_lints::INTERNING_DEFINED_SYMBOL,
     #[cfg(feature = "internal-lints")]
+    utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE,
+    #[cfg(feature = "internal-lints")]
     utils::internal_lints::INVALID_PATHS,
     #[cfg(feature = "internal-lints")]
     utils::internal_lints::LINT_WITHOUT_LINT_PASS,
     #[cfg(feature = "internal-lints")]
     utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
     #[cfg(feature = "internal-lints")]
+    utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
+    #[cfg(feature = "internal-lints")]
     utils::internal_lints::OUTER_EXPN_EXPN_DATA,
     #[cfg(feature = "internal-lints")]
     utils::internal_lints::PRODUCE_ICE,
     default::FIELD_REASSIGN_WITH_DEFAULT,
     default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
     dereference::EXPLICIT_DEREF_METHODS,
+    dereference::NEEDLESS_BORROW,
+    dereference::REF_BINDING_TO_REFERENCE,
     derivable_impls::DERIVABLE_IMPLS,
     derive::DERIVE_HASH_XOR_EQ,
     derive::DERIVE_ORD_XOR_PARTIAL_ORD,
     derive::EXPL_IMPL_CLONE_ON_COPY,
     derive::UNSAFE_DERIVE_DESERIALIZE,
-    disallowed_method::DISALLOWED_METHOD,
+    disallowed_methods::DISALLOWED_METHODS,
     disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
-    disallowed_type::DISALLOWED_TYPE,
+    disallowed_types::DISALLOWED_TYPES,
     doc::DOC_MARKDOWN,
     doc::MISSING_ERRORS_DOC,
     doc::MISSING_PANICS_DOC,
     implicit_return::IMPLICIT_RETURN,
     implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
     inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
+    index_refutable_slice::INDEX_REFUTABLE_SLICE,
     indexing_slicing::INDEXING_SLICING,
     indexing_slicing::OUT_OF_BOUNDS_INDEXING,
     infinite_iter::INFINITE_ITER,
     methods::MAP_FLATTEN,
     methods::MAP_IDENTITY,
     methods::MAP_UNWRAP_OR,
+    methods::NEEDLESS_SPLITN,
     methods::NEW_RET_NO_SELF,
     methods::OK_EXPECT,
     methods::OPTION_AS_REF_DEREF,
     needless_bitwise_bool::NEEDLESS_BITWISE_BOOL,
     needless_bool::BOOL_COMPARISON,
     needless_bool::NEEDLESS_BOOL,
-    needless_borrow::NEEDLESS_BORROW,
-    needless_borrow::REF_BINDING_TO_REFERENCE,
     needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
     needless_continue::NEEDLESS_CONTINUE,
     needless_for_each::NEEDLESS_FOR_EACH,
+    needless_late_init::NEEDLESS_LATE_INIT,
     needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF,
     needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
     needless_question_mark::NEEDLESS_QUESTION_MARK,
     non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
     non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
     nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
+    octal_escapes::OCTAL_ESCAPES,
     open_options::NONSENSICAL_OPEN_OPTIONS,
     option_env_unwrap::OPTION_ENV_UNWRAP,
     option_if_let_else::OPTION_IF_LET_ELSE,
index 44c75a11eec08111c17db1d2ca03862a17e3a6a3..59182fd8175d404bfbd9e68b070445419c7fa1bf 100644 (file)
@@ -6,13 +6,14 @@
     LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
     LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
     LintId::of(copies::BRANCHES_SHARING_CODE),
-    LintId::of(disallowed_method::DISALLOWED_METHOD),
-    LintId::of(disallowed_type::DISALLOWED_TYPE),
+    LintId::of(disallowed_methods::DISALLOWED_METHODS),
+    LintId::of(disallowed_types::DISALLOWED_TYPES),
     LintId::of(equatable_if_let::EQUATABLE_IF_LET),
     LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
     LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
     LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
     LintId::of(future_not_send::FUTURE_NOT_SEND),
+    LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
     LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
     LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
     LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
index 404ca20b5abc62bf05adf9b474a19c0990048e00..70a4a624378909dd1040fbe9997927c07fa6d8c2 100644 (file)
@@ -21,6 +21,7 @@
     LintId::of(copy_iterator::COPY_ITERATOR),
     LintId::of(default::DEFAULT_TRAIT_ACCESS),
     LintId::of(dereference::EXPLICIT_DEREF_METHODS),
+    LintId::of(dereference::REF_BINDING_TO_REFERENCE),
     LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
     LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
     LintId::of(doc::DOC_MARKDOWN),
     LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
     LintId::of(methods::IMPLICIT_CLONE),
     LintId::of(methods::INEFFICIENT_TO_STRING),
-    LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_UNWRAP_OR),
     LintId::of(misc::FLOAT_CMP),
     LintId::of(misc::USED_UNDERSCORE_BINDING),
     LintId::of(mut_mut::MUT_MUT),
     LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
-    LintId::of(needless_borrow::REF_BINDING_TO_REFERENCE),
     LintId::of(needless_continue::NEEDLESS_CONTINUE),
     LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
     LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
index 744880bda3e69c2a83763dc5d9b1a38ade7b6ec5..ea87e7e7a73687bb37b9385396c6c4ebc5c2b3a2 100644 (file)
@@ -15,6 +15,7 @@
     LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
     LintId::of(comparison_chain::COMPARISON_CHAIN),
     LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+    LintId::of(dereference::NEEDLESS_BORROW),
     LintId::of(doc::MISSING_SAFETY_DOC),
     LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
     LintId::of(enum_variants::ENUM_VARIANT_NAMES),
@@ -81,7 +82,7 @@
     LintId::of(misc_early::REDUNDANT_PATTERN),
     LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
     LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
-    LintId::of(needless_borrow::NEEDLESS_BORROW),
+    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
     LintId::of(neg_multiply::NEG_MULTIPLY),
     LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
     LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
index a3f964d1580428f92230f26f169586fd86e25504..414bfc42fdfcdabe6fecf7e701c1474b61ccf774 100644 (file)
@@ -16,6 +16,7 @@
     LintId::of(methods::SUSPICIOUS_MAP),
     LintId::of(mut_key::MUTABLE_KEY_TYPE),
     LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
+    LintId::of(octal_escapes::OCTAL_ESCAPES),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 ])
index 7174d0a082e0f7627f062180d10f5ef47c170a4b..bd9710ec40750e23b89f2aed7911bbc00df09778 100644 (file)
@@ -8,6 +8,7 @@
 #![feature(rustc_private)]
 #![feature(stmt_expr_attributes)]
 #![feature(control_flow_enum)]
+#![feature(let_else)]
 #![recursion_limit = "512"]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
@@ -153,6 +154,10 @@ macro_rules! declare_clippy_lint {
 
 #[cfg(feature = "metadata-collector-lint")]
 mod deprecated_lints;
+#[cfg_attr(
+    any(feature = "internal-lints", feature = "metadata-collector-lint"),
+    allow(clippy::missing_clippy_version_attribute)
+)]
 mod utils;
 
 // begin lints modules, do not remove this comment, it’s used in `update_lints`
@@ -189,9 +194,9 @@ macro_rules! declare_clippy_lint {
 mod dereference;
 mod derivable_impls;
 mod derive;
-mod disallowed_method;
+mod disallowed_methods;
 mod disallowed_script_idents;
-mod disallowed_type;
+mod disallowed_types;
 mod doc;
 mod double_comparison;
 mod double_parens;
@@ -233,6 +238,7 @@ macro_rules! declare_clippy_lint {
 mod implicit_return;
 mod implicit_saturating_sub;
 mod inconsistent_struct_constructor;
+mod index_refutable_slice;
 mod indexing_slicing;
 mod infinite_iter;
 mod inherent_impl;
@@ -290,10 +296,10 @@ macro_rules! declare_clippy_lint {
 mod needless_arbitrary_self_type;
 mod needless_bitwise_bool;
 mod needless_bool;
-mod needless_borrow;
 mod needless_borrowed_ref;
 mod needless_continue;
 mod needless_for_each;
+mod needless_late_init;
 mod needless_option_as_deref;
 mod needless_pass_by_value;
 mod needless_question_mark;
@@ -307,6 +313,7 @@ macro_rules! declare_clippy_lint {
 mod non_octal_unix_permissions;
 mod non_send_fields_in_send_ty;
 mod nonstandard_macro_braces;
+mod octal_escapes;
 mod open_options;
 mod option_env_unwrap;
 mod option_if_let_else;
@@ -404,10 +411,21 @@ macro_rules! declare_clippy_lint {
 /// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
 ///
 /// Used in `./src/driver.rs`.
-pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) {
+pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
     // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
+
+    let msrv = conf.msrv.as_ref().and_then(|s| {
+        parse_msrv(s, None, None).or_else(|| {
+            sess.err(&format!(
+                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
+                s
+            ));
+            None
+        })
+    });
+
     store.register_pre_expansion_pass(|| Box::new(write::Write::default()));
-    store.register_pre_expansion_pass(|| Box::new(attrs::EarlyAttributes));
+    store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
     store.register_pre_expansion_pass(|| Box::new(dbg_macro::DbgMacro));
 }
 
@@ -441,7 +459,6 @@ pub fn read_conf(sess: &Session) -> Conf {
 ///
 /// Used in `./src/driver.rs`.
 #[allow(clippy::too_many_lines)]
-#[rustfmt::skip]
 pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
     register_removed_non_tool_lints(store);
 
@@ -493,11 +510,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     let vec_box_size_threshold = conf.vec_box_size_threshold;
     let type_complexity_threshold = conf.type_complexity_threshold;
     let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
-    store.register_late_pass(move || Box::new(types::Types::new(
-        vec_box_size_threshold,
-        type_complexity_threshold,
-        avoid_breaking_exported_api,
-    )));
+    store.register_late_pass(move || {
+        Box::new(types::Types::new(
+            vec_box_size_threshold,
+            type_complexity_threshold,
+            avoid_breaking_exported_api,
+        ))
+    });
     store.register_late_pass(|| Box::new(booleans::NonminimalBool));
     store.register_late_pass(|| Box::new(needless_bitwise_bool::NeedlessBitwiseBool));
     store.register_late_pass(|| Box::new(eq_op::EqOp));
@@ -535,7 +554,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
 
     let msrv = conf.msrv.as_ref().and_then(|s| {
         parse_msrv(s, None, None).or_else(|| {
-            sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s));
+            sess.err(&format!(
+                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
+                s
+            ));
             None
         })
     });
@@ -560,6 +582,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
 
     store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
     store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
+    let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
+    store.register_late_pass(move || {
+        Box::new(index_refutable_slice::IndexRefutableSlice::new(
+            max_suggested_slice_pattern_length,
+            msrv,
+        ))
+    });
     store.register_late_pass(|| Box::new(map_clone::MapClone));
     store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
     store.register_late_pass(|| Box::new(shadow::Shadow::default()));
@@ -573,16 +602,19 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
     store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
     store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
-    store.register_late_pass(|| Box::new(needless_borrow::NeedlessBorrow::default()));
     store.register_late_pass(|| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
     store.register_late_pass(|| Box::new(no_effect::NoEffect));
     store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment));
     store.register_late_pass(|| Box::new(transmute::Transmute));
     let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
-    store.register_late_pass(move || Box::new(cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold)));
+    store.register_late_pass(move || {
+        Box::new(cognitive_complexity::CognitiveComplexity::new(
+            cognitive_complexity_threshold,
+        ))
+    });
     let too_large_for_stack = conf.too_large_for_stack;
-    store.register_late_pass(move || Box::new(escape::BoxedLocal{too_large_for_stack}));
-    store.register_late_pass(move || Box::new(vec::UselessVec{too_large_for_stack}));
+    store.register_late_pass(move || Box::new(escape::BoxedLocal { too_large_for_stack }));
+    store.register_late_pass(move || Box::new(vec::UselessVec { too_large_for_stack }));
     store.register_late_pass(|| Box::new(panic_unimplemented::PanicUnimplemented));
     store.register_late_pass(|| Box::new(strings::StringLitAsBytes));
     store.register_late_pass(|| Box::new(derive::Derive));
@@ -603,7 +635,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(blacklisted_name::BlacklistedName::new(blacklisted_names.clone())));
     let too_many_arguments_threshold = conf.too_many_arguments_threshold;
     let too_many_lines_threshold = conf.too_many_lines_threshold;
-    store.register_late_pass(move || Box::new(functions::Functions::new(too_many_arguments_threshold, too_many_lines_threshold)));
+    store.register_late_pass(move || {
+        Box::new(functions::Functions::new(
+            too_many_arguments_threshold,
+            too_many_lines_threshold,
+        ))
+    });
     let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
     store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
     store.register_late_pass(|| Box::new(neg_multiply::NegMultiply));
@@ -688,14 +725,32 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(multiple_crate_versions::MultipleCrateVersions));
     store.register_late_pass(|| Box::new(wildcard_dependencies::WildcardDependencies));
     let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
-    store.register_early_pass(move || Box::new(literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability)));
+    store.register_early_pass(move || {
+        Box::new(literal_representation::LiteralDigitGrouping::new(
+            literal_representation_lint_fraction_readability,
+        ))
+    });
     let literal_representation_threshold = conf.literal_representation_threshold;
-    store.register_early_pass(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)));
+    store.register_early_pass(move || {
+        Box::new(literal_representation::DecimalLiteralRepresentation::new(
+            literal_representation_threshold,
+        ))
+    });
     let enum_variant_name_threshold = conf.enum_variant_name_threshold;
-    store.register_late_pass(move || Box::new(enum_variants::EnumVariantNames::new(enum_variant_name_threshold, avoid_breaking_exported_api)));
+    store.register_late_pass(move || {
+        Box::new(enum_variants::EnumVariantNames::new(
+            enum_variant_name_threshold,
+            avoid_breaking_exported_api,
+        ))
+    });
     store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
     let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
-    store.register_late_pass(move || Box::new(upper_case_acronyms::UpperCaseAcronyms::new(avoid_breaking_exported_api, upper_case_acronyms_aggressive)));
+    store.register_late_pass(move || {
+        Box::new(upper_case_acronyms::UpperCaseAcronyms::new(
+            avoid_breaking_exported_api,
+            upper_case_acronyms_aggressive,
+        ))
+    });
     store.register_late_pass(|| Box::new(default::Default::default()));
     store.register_late_pass(|| Box::new(unused_self::UnusedSelf));
     store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
@@ -710,7 +765,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports));
     let max_fn_params_bools = conf.max_fn_params_bools;
     let max_struct_bools = conf.max_struct_bools;
-    store.register_early_pass(move || Box::new(excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools)));
+    store.register_early_pass(move || {
+        Box::new(excessive_bools::ExcessiveBools::new(
+            max_struct_bools,
+            max_fn_params_bools,
+        ))
+    });
     store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
     let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
     store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
@@ -729,9 +789,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
     store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
     let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
-    store.register_early_pass(move || Box::new(non_expressive_names::NonExpressiveNames {
-        single_char_binding_names_threshold,
-    }));
+    store.register_early_pass(move || {
+        Box::new(non_expressive_names::NonExpressiveNames {
+            single_char_binding_names_threshold,
+        })
+    });
     let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>();
     store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(&macro_matcher)));
     store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
@@ -746,7 +808,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
     store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
     let disallowed_methods = conf.disallowed_methods.clone();
-    store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(disallowed_methods.clone())));
+    store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
     store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
     store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
     store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops));
@@ -754,7 +816,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(strings::StringToString));
     store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
     store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
-    store.register_late_pass(|| Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons));
+    store.register_late_pass(|| {
+        Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
+    });
     store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
     store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
     store.register_late_pass(|| Box::new(manual_map::ManualMap));
@@ -763,9 +827,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(move || Box::new(module_style::ModStyle));
     store.register_late_pass(|| Box::new(unused_async::UnusedAsync));
     let disallowed_types = conf.disallowed_types.clone();
-    store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(disallowed_types.clone())));
+    store.register_late_pass(move || Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone())));
     let import_renames = conf.enforced_import_renames.clone();
-    store.register_late_pass(move || Box::new(missing_enforced_import_rename::ImportRename::new(import_renames.clone())));
+    store.register_late_pass(move || {
+        Box::new(missing_enforced_import_rename::ImportRename::new(
+            import_renames.clone(),
+        ))
+    });
     let scripts = conf.allowed_scripts.clone();
     store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts)));
     store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings));
@@ -774,11 +842,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator));
     store.register_late_pass(move || Box::new(manual_assert::ManualAssert));
     let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send;
-    store.register_late_pass(move || Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(enable_raw_pointer_heuristic_for_send)));
+    store.register_late_pass(move || {
+        Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(
+            enable_raw_pointer_heuristic_for_send,
+        ))
+    });
     store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default()));
     store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch));
     store.register_late_pass(move || Box::new(format_args::FormatArgs));
     store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray));
+    store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
+    store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
@@ -852,6 +926,8 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
     ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
     ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
     ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");
+    ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types");
+    ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods");
 
     // uplifted lints
     ls.register_renamed("clippy::invalid_ref", "invalid_value");
index cb0b96e0652e53b1115b238a63713907c8a7fa04..fad3343d128510aca02466f18960f65a251932b2 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{in_macro, trait_ref_of_method};
+use clippy_utils::trait_ref_of_method;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::intravisit::{
     walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty,
@@ -45,6 +45,7 @@
     ///     x
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_LIFETIMES,
     complexity,
     "using explicit lifetimes for references in function arguments when elision rules \
@@ -73,6 +74,7 @@
     ///     // ...
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXTRA_UNUSED_LIFETIMES,
     complexity,
     "unused lifetimes in function definitions"
@@ -128,7 +130,7 @@ fn check_fn_inner<'tcx>(
     span: Span,
     report_extra_lifetimes: bool,
 ) {
-    if in_macro(span) || has_where_lifetimes(cx, &generics.where_clause) {
+    if span.from_expansion() || has_where_lifetimes(cx, &generics.where_clause) {
         return;
     }
 
index 0e5121ca3d73a9d0e5e0107aca96ef4c46f91a10..130543bbbee80ef229aa818188f9bab24e191c2f 100644 (file)
@@ -2,11 +2,8 @@
 //! floating-point literal expressions.
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::numeric_literal::{NumericLiteral, Radix};
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{
-    in_macro,
-    numeric_literal::{NumericLiteral, Radix},
-};
 use if_chain::if_chain;
 use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
 use rustc_errors::Applicability;
@@ -31,6 +28,7 @@
     /// // Good
     /// let x: u64 = 61_864_918_973_511;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNREADABLE_LITERAL,
     pedantic,
     "long literal without underscores"
@@ -56,6 +54,7 @@
     /// // Good
     /// 2_i32;
     /// ```
+    #[clippy::version = "1.30.0"]
     pub MISTYPED_LITERAL_SUFFIXES,
     correctness,
     "mistyped literal suffix"
@@ -78,6 +77,7 @@
     /// // Good
     /// let x: u64 = 61_864_918_973_511;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INCONSISTENT_DIGIT_GROUPING,
     style,
     "integer literals with digits grouped inconsistently"
@@ -96,6 +96,7 @@
     /// let x: u32 = 0xFFF_FFF;
     /// let y: u8 = 0b01_011_101;
     /// ```
+    #[clippy::version = "1.49.0"]
     pub UNUSUAL_BYTE_GROUPINGS,
     style,
     "binary or hex literals that aren't grouped by four"
     /// ```rust
     /// let x: u64 = 6186491_8973511;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LARGE_DIGIT_GROUPS,
     pedantic,
     "grouping digits into groups that are too large"
     /// `255` => `0xFF`
     /// `65_535` => `0xFFFF`
     /// `4_042_322_160` => `0xF0F0_F0F0`
+    #[clippy::version = "pre 1.29.0"]
     pub DECIMAL_LITERAL_REPRESENTATION,
     restriction,
     "using decimal representation when hexadecimal would be better"
@@ -283,7 +286,7 @@ fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
                         | WarningType::InconsistentDigitGrouping
                         | WarningType::UnusualByteGroupings
                         | WarningType::LargeDigitGroups => {
-                            !in_macro(lit.span)
+                            !lit.span.from_expansion()
                         }
                         WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => {
                             true
index 6f213d7a699030e9852bae7f6ca30c70df28eda2..e0150990cfe5fb42ee247f996df71f33d101893b 100644 (file)
@@ -1,5 +1,5 @@
 use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP};
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{get_enclosing_block, is_integer_const};
 use if_chain::if_chain;
@@ -7,6 +7,7 @@
 use rustc_hir::intravisit::{walk_block, walk_expr};
 use rustc_hir::{Expr, Pat};
 use rustc_lint::LateContext;
+use rustc_middle::ty::{self, UintTy};
 
 // To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
 // incremented exactly once in the loop body, and initialized to zero
@@ -30,24 +31,59 @@ pub(super) fn check<'tcx>(
             walk_block(&mut initialize_visitor, block);
 
             if_chain! {
-                if let Some((name, initializer)) = initialize_visitor.get_result();
+                if let Some((name, ty, initializer)) = initialize_visitor.get_result();
                 if is_integer_const(cx, initializer, 0);
                 then {
                     let mut applicability = Applicability::MachineApplicable;
 
-                    span_lint_and_sugg(
+                    let int_name = match ty.map(ty::TyS::kind) {
+                        // usize or inferred
+                        Some(ty::Uint(UintTy::Usize)) | None => {
+                            span_lint_and_sugg(
+                                cx,
+                                EXPLICIT_COUNTER_LOOP,
+                                expr.span.with_hi(arg.span.hi()),
+                                &format!("the variable `{}` is used as a loop counter", name),
+                                "consider using",
+                                format!(
+                                    "for ({}, {}) in {}.enumerate()",
+                                    name,
+                                    snippet_with_applicability(cx, pat.span, "item", &mut applicability),
+                                    make_iterator_snippet(cx, arg, &mut applicability),
+                                ),
+                                applicability,
+                            );
+                            return;
+                        }
+                        Some(ty::Int(int_ty)) => int_ty.name_str(),
+                        Some(ty::Uint(uint_ty)) => uint_ty.name_str(),
+                        _ => return,
+                    };
+
+                    span_lint_and_then(
                         cx,
                         EXPLICIT_COUNTER_LOOP,
                         expr.span.with_hi(arg.span.hi()),
                         &format!("the variable `{}` is used as a loop counter", name),
-                        "consider using",
-                        format!(
-                            "for ({}, {}) in {}.enumerate()",
-                            name,
-                            snippet_with_applicability(cx, pat.span, "item", &mut applicability),
-                            make_iterator_snippet(cx, arg, &mut applicability),
-                        ),
-                        applicability,
+                        |diag| {
+                            diag.span_suggestion(
+                                expr.span.with_hi(arg.span.hi()),
+                                "consider using",
+                                format!(
+                                    "for ({}, {}) in (0_{}..).zip({})",
+                                    name,
+                                    snippet_with_applicability(cx, pat.span, "item", &mut applicability),
+                                    int_name,
+                                    make_iterator_snippet(cx, arg, &mut applicability),
+                                ),
+                                applicability,
+                            );
+
+                            diag.note(&format!(
+                                "`{}` is of type `{}`, making it ineligible for `Iterator::enumerate`",
+                                name, int_name
+                            ));
+                        },
                     );
                 }
             }
index 2362b4b20670f842b2cacebc17bdcfc02729a0a8..6cda926853438bd63163da747c2d6c1c4f6eafcb 100644 (file)
@@ -445,7 +445,7 @@ fn get_loop_counters<'a, 'tcx>(
                 let mut initialize_visitor = InitializeVisitor::new(cx, expr, var_id);
                 walk_block(&mut initialize_visitor, block);
 
-                initialize_visitor.get_result().map(|(_, initializer)| Start {
+                initialize_visitor.get_result().map(|(_, _, initializer)| Start {
                     id: var_id,
                     kind: StartKind::Counter { initializer },
                 })
index fd4881b29474f95b36a7df6c8eb30171010ae362..e2f9aee063dd2f8dea4f553ff7e3b5af1c77b64b 100644 (file)
@@ -47,6 +47,7 @@
     /// # let mut dst = vec![0; 65];
     /// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MANUAL_MEMCPY,
     perf,
     "manually copying items between slices"
@@ -75,6 +76,7 @@
     ///     println!("{}", i);
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_RANGE_LOOP,
     style,
     "for-looping over a range of indices where an iterator over items would do"
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPLICIT_ITER_LOOP,
     pedantic,
     "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPLICIT_INTO_ITER_LOOP,
     pedantic,
     "for-looping over `_.into_iter()` when `_` would do"
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_NEXT_LOOP,
     correctness,
     "for-looping over `_.next()` which is probably not intended"
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub FOR_LOOPS_OVER_FALLIBLES,
     suspicious,
     "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
     ///     // .. do something with x
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WHILE_LET_LOOP,
     complexity,
     "`loop { if let { ... } else break }`, which can be written as a `while let` loop"
     /// // should be
     /// let len = iterator.count();
     /// ```
+    #[clippy::version = "1.30.0"]
     pub NEEDLESS_COLLECT,
     perf,
     "collecting an iterator when collect is not needed"
     /// # fn bar(bar: usize, baz: usize) {}
     /// for (i, item) in v.iter().enumerate() { bar(i, *item); }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPLICIT_COUNTER_LOOP,
     complexity,
     "for-looping with an explicit counter when `_.enumerate()` would do"
     /// ```no_run
     /// loop {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EMPTY_LOOP,
     suspicious,
     "empty `loop {}`, which should block or sleep"
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WHILE_LET_ON_ITERATOR,
     style,
     "using a `while let` loop instead of a for loop on an iterator"
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FOR_KV_MAP,
     style,
     "looping on a map using `iter` when `keys` or `values` would do"
     ///     break;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEVER_LOOP,
     correctness,
     "any loop that will always `break` or `return`"
     ///     println!("{}", i); // prints numbers from 0 to 42, not 0 to 21
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUT_RANGE_BOUND,
     suspicious,
     "for loop over a range where one of the bounds is a mutable variable"
     ///     println!("let me loop forever!");
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WHILE_IMMUTABLE_CONDITION,
     correctness,
     "variables used within while expression are not mutated in the body"
     /// let mut vec: Vec<u8> = vec![item1; 20];
     /// vec.resize(20 + 30, item2);
     /// ```
+    #[clippy::version = "1.47.0"]
     pub SAME_ITEM_PUSH,
     style,
     "the same item is pushed inside of a for loop"
     /// let item = &item1;
     /// println!("{}", item);
     /// ```
+    #[clippy::version = "1.49.0"]
     pub SINGLE_ELEMENT_LOOP,
     complexity,
     "there is no reason to have a single element loop"
     ///     println!("{}", n);
     /// }
     /// ```
+    #[clippy::version = "1.52.0"]
     pub MANUAL_FLATTEN,
     complexity,
     "for loops over `Option`s or `Result`s with a single expression can be simplified"
index e87f4b669124f6e6c8fe2e3515e2fa1622972da2..6f3acb45ba4f89e55c24f5600d238b152d4d7940 100644 (file)
@@ -3,13 +3,16 @@
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_trait_method, path_to_local_id};
+use clippy_utils::{can_move_expr_to_closure, is_trait_method, path_to_local, path_to_local_id, CaptureKind};
 use if_chain::if_chain;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, StmtKind};
+use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, TyS};
 use rustc_span::sym;
 use rustc_span::{MultiSpan, Span};
 
@@ -83,7 +86,8 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo
                     is_type_diagnostic_item(cx, ty, sym::VecDeque) ||
                     is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
                     is_type_diagnostic_item(cx, ty, sym::LinkedList);
-                if let Some(iter_calls) = detect_iter_and_into_iters(block, id);
+                let iter_ty = cx.typeck_results().expr_ty(iter_source);
+                if let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty));
                 if let [iter_call] = &*iter_calls;
                 then {
                     let mut used_count_visitor = UsedCountVisitor {
@@ -167,37 +171,89 @@ enum IterFunctionKind {
     Contains(Span),
 }
 
-struct IterFunctionVisitor {
-    uses: Vec<IterFunction>,
+struct IterFunctionVisitor<'a, 'tcx> {
+    illegal_mutable_capture_ids: HirIdSet,
+    current_mutably_captured_ids: HirIdSet,
+    cx: &'a LateContext<'tcx>,
+    uses: Vec<Option<IterFunction>>,
+    hir_id_uses_map: FxHashMap<HirId, usize>,
+    current_statement_hir_id: Option<HirId>,
     seen_other: bool,
     target: HirId,
 }
-impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
+impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
+    fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
+        for (expr, hir_id) in block.stmts.iter().filter_map(get_expr_and_hir_id_from_stmt) {
+            self.visit_block_expr(expr, hir_id);
+        }
+        if let Some(expr) = block.expr {
+            self.visit_block_expr(expr, None);
+        }
+    }
+
     fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
         // Check function calls on our collection
         if let ExprKind::MethodCall(method_name, _, [recv, args @ ..], _) = &expr.kind {
+            if method_name.ident.name == sym!(collect) && is_trait_method(self.cx, expr, sym::Iterator) {
+                self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv));
+                self.visit_expr(recv);
+                return;
+            }
+
             if path_to_local_id(recv, self.target) {
-                match &*method_name.ident.name.as_str() {
-                    "into_iter" => self.uses.push(IterFunction {
-                        func: IterFunctionKind::IntoIter,
-                        span: expr.span,
-                    }),
-                    "len" => self.uses.push(IterFunction {
-                        func: IterFunctionKind::Len,
-                        span: expr.span,
-                    }),
-                    "is_empty" => self.uses.push(IterFunction {
-                        func: IterFunctionKind::IsEmpty,
-                        span: expr.span,
-                    }),
-                    "contains" => self.uses.push(IterFunction {
-                        func: IterFunctionKind::Contains(args[0].span),
-                        span: expr.span,
-                    }),
-                    _ => self.seen_other = true,
+                if self
+                    .illegal_mutable_capture_ids
+                    .intersection(&self.current_mutably_captured_ids)
+                    .next()
+                    .is_none()
+                {
+                    if let Some(hir_id) = self.current_statement_hir_id {
+                        self.hir_id_uses_map.insert(hir_id, self.uses.len());
+                    }
+                    match &*method_name.ident.name.as_str() {
+                        "into_iter" => self.uses.push(Some(IterFunction {
+                            func: IterFunctionKind::IntoIter,
+                            span: expr.span,
+                        })),
+                        "len" => self.uses.push(Some(IterFunction {
+                            func: IterFunctionKind::Len,
+                            span: expr.span,
+                        })),
+                        "is_empty" => self.uses.push(Some(IterFunction {
+                            func: IterFunctionKind::IsEmpty,
+                            span: expr.span,
+                        })),
+                        "contains" => self.uses.push(Some(IterFunction {
+                            func: IterFunctionKind::Contains(args[0].span),
+                            span: expr.span,
+                        })),
+                        _ => {
+                            self.seen_other = true;
+                            if let Some(hir_id) = self.current_statement_hir_id {
+                                self.hir_id_uses_map.remove(&hir_id);
+                            }
+                        },
+                    }
                 }
                 return;
             }
+
+            if let Some(hir_id) = path_to_local(recv) {
+                if let Some(index) = self.hir_id_uses_map.remove(&hir_id) {
+                    if self
+                        .illegal_mutable_capture_ids
+                        .intersection(&self.current_mutably_captured_ids)
+                        .next()
+                        .is_none()
+                    {
+                        if let Some(hir_id) = self.current_statement_hir_id {
+                            self.hir_id_uses_map.insert(hir_id, index);
+                        }
+                    } else {
+                        self.uses[index] = None;
+                    }
+                }
+            }
         }
         // Check if the collection is used for anything else
         if path_to_local_id(expr, self.target) {
@@ -213,6 +269,28 @@ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
     }
 }
 
+impl<'tcx> IterFunctionVisitor<'_, 'tcx> {
+    fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) {
+        self.current_statement_hir_id = hir_id;
+        self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(expr));
+        self.visit_expr(expr);
+    }
+}
+
+fn get_expr_and_hir_id_from_stmt<'v>(stmt: &'v Stmt<'v>) -> Option<(&'v Expr<'v>, Option<HirId>)> {
+    match stmt.kind {
+        StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some((expr, None)),
+        StmtKind::Item(..) => None,
+        StmtKind::Local(Local { init, pat, .. }) => {
+            if let PatKind::Binding(_, hir_id, ..) = pat.kind {
+                init.map(|init_expr| (init_expr, Some(hir_id)))
+            } else {
+                init.map(|init_expr| (init_expr, None))
+            }
+        },
+    }
+}
+
 struct UsedCountVisitor<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     id: HirId,
@@ -237,12 +315,60 @@ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 
 /// Detect the occurrences of calls to `iter` or `into_iter` for the
 /// given identifier
-fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, id: HirId) -> Option<Vec<IterFunction>> {
+fn detect_iter_and_into_iters<'tcx: 'a, 'a>(
+    block: &'tcx Block<'tcx>,
+    id: HirId,
+    cx: &'a LateContext<'tcx>,
+    captured_ids: HirIdSet,
+) -> Option<Vec<IterFunction>> {
     let mut visitor = IterFunctionVisitor {
         uses: Vec::new(),
         target: id,
         seen_other: false,
+        cx,
+        current_mutably_captured_ids: HirIdSet::default(),
+        illegal_mutable_capture_ids: captured_ids,
+        hir_id_uses_map: FxHashMap::default(),
+        current_statement_hir_id: None,
     };
     visitor.visit_block(block);
-    if visitor.seen_other { None } else { Some(visitor.uses) }
+    if visitor.seen_other {
+        None
+    } else {
+        Some(visitor.uses.into_iter().flatten().collect())
+    }
+}
+
+fn get_captured_ids(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>) -> HirIdSet {
+    fn get_captured_ids_recursive(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>, set: &mut HirIdSet) {
+        match ty.kind() {
+            ty::Adt(_, generics) => {
+                for generic in *generics {
+                    if let GenericArgKind::Type(ty) = generic.unpack() {
+                        get_captured_ids_recursive(cx, ty, set);
+                    }
+                }
+            },
+            ty::Closure(def_id, _) => {
+                let closure_hir_node = cx.tcx.hir().get_if_local(*def_id).unwrap();
+                if let Node::Expr(closure_expr) = closure_hir_node {
+                    can_move_expr_to_closure(cx, closure_expr)
+                        .unwrap()
+                        .into_iter()
+                        .for_each(|(hir_id, capture_kind)| {
+                            if matches!(capture_kind, CaptureKind::Ref(Mutability::Mut)) {
+                                set.insert(hir_id);
+                            }
+                        });
+                }
+            },
+            _ => (),
+        }
+    }
+
+    let mut set = HirIdSet::default();
+
+    get_captured_ids_recursive(cx, ty, &mut set);
+
+    set
 }
index c3939a66c6accdced10229bdc3612975b71c03cf..f6b7e1bc353fd24c6b9c736a52a93fb7d2823f88 100644 (file)
@@ -1,13 +1,16 @@
 use clippy_utils::ty::{has_iter_method, implements_trait};
 use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg};
 use if_chain::if_chain;
+use rustc_ast::ast::{LitIntType, LitKind};
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
-use rustc_hir::HirIdMap;
-use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Stmt, StmtKind};
+use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
+use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
+use rustc_middle::ty::Ty;
+use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{sym, Symbol};
+use rustc_typeck::hir_ty_to_ty;
 use std::iter::Iterator;
 
 #[derive(Debug, PartialEq)]
@@ -105,10 +108,11 @@ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 }
 
 enum InitializeVisitorState<'hir> {
-    Initial,          // Not examined yet
-    Declared(Symbol), // Declared but not (yet) initialized
+    Initial,                            // Not examined yet
+    Declared(Symbol, Option<Ty<'hir>>), // Declared but not (yet) initialized
     Initialized {
         name: Symbol,
+        ty: Option<Ty<'hir>>,
         initializer: &'hir Expr<'hir>,
     },
     DontWarn,
@@ -137,9 +141,9 @@ pub(super) fn new(cx: &'a LateContext<'tcx>, end_expr: &'tcx Expr<'tcx>, var_id:
         }
     }
 
-    pub(super) fn get_result(&self) -> Option<(Symbol, &'tcx Expr<'tcx>)> {
-        if let InitializeVisitorState::Initialized { name, initializer } = self.state {
-            Some((name, initializer))
+    pub(super) fn get_result(&self) -> Option<(Symbol, Option<Ty<'tcx>>, &'tcx Expr<'tcx>)> {
+        if let InitializeVisitorState::Initialized { name, ty, initializer } = self.state {
+            Some((name, ty, initializer))
         } else {
             None
         }
@@ -149,22 +153,25 @@ pub(super) fn get_result(&self) -> Option<(Symbol, &'tcx Expr<'tcx>)> {
 impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
     type Map = Map<'tcx>;
 
-    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
+    fn visit_local(&mut self, l: &'tcx Local<'_>) {
         // Look for declarations of the variable
         if_chain! {
-            if let StmtKind::Local(local) = stmt.kind;
-            if local.pat.hir_id == self.var_id;
-            if let PatKind::Binding(.., ident, _) = local.pat.kind;
+            if l.pat.hir_id == self.var_id;
+            if let PatKind::Binding(.., ident, _) = l.pat.kind;
             then {
-                self.state = local.init.map_or(InitializeVisitorState::Declared(ident.name), |init| {
+                let ty = l.ty.map(|ty| hir_ty_to_ty(self.cx.tcx, ty));
+
+                self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| {
                     InitializeVisitorState::Initialized {
                         initializer: init,
+                        ty,
                         name: ident.name,
                     }
                 })
             }
         }
-        walk_stmt(self, stmt);
+
+        walk_local(self, l);
     }
 
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
@@ -194,15 +201,38 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
                         self.state = InitializeVisitorState::DontWarn;
                     },
                     ExprKind::Assign(lhs, rhs, _) if lhs.hir_id == expr.hir_id => {
-                        self.state = if_chain! {
-                            if self.depth == 0;
-                            if let InitializeVisitorState::Declared(name)
-                                | InitializeVisitorState::Initialized { name, ..} = self.state;
-                            then {
-                                InitializeVisitorState::Initialized { initializer: rhs, name }
-                            } else {
-                                InitializeVisitorState::DontWarn
+                        self.state = if self.depth == 0 {
+                            match self.state {
+                                InitializeVisitorState::Declared(name, mut ty) => {
+                                    if ty.is_none() {
+                                        if let ExprKind::Lit(Spanned {
+                                            node: LitKind::Int(_, LitIntType::Unsuffixed),
+                                            ..
+                                        }) = rhs.kind
+                                        {
+                                            ty = None;
+                                        } else {
+                                            ty = self.cx.typeck_results().expr_ty_opt(rhs);
+                                        }
+                                    }
+
+                                    InitializeVisitorState::Initialized {
+                                        initializer: rhs,
+                                        ty,
+                                        name,
+                                    }
+                                },
+                                InitializeVisitorState::Initialized { ty, name, .. } => {
+                                    InitializeVisitorState::Initialized {
+                                        initializer: rhs,
+                                        ty,
+                                        name,
+                                    }
+                                },
+                                _ => InitializeVisitorState::DontWarn,
                             }
+                        } else {
+                            InitializeVisitorState::DontWarn
                         }
                     },
                     ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
index c38162743a3ea654311f802ccd62ab9b48ed8159..5b22b64a370e251ea56ff9f1ee1894428b3b03cb 100644 (file)
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use clippy_utils::source::snippet;
 use hir::def::{DefKind, Res};
 use if_chain::if_chain;
@@ -9,6 +8,7 @@
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::hygiene::ExpnKind;
 use rustc_span::{edition::Edition, sym, Span};
 
 declare_clippy_lint! {
@@ -24,6 +24,7 @@
     /// #[macro_use]
     /// use some_macro;
     /// ```
+    #[clippy::version = "1.44.0"]
     pub MACRO_USE_IMPORTS,
     pedantic,
     "#[macro_use] is no longer needed"
@@ -212,3 +213,7 @@ fn check_crate_post(&mut self, cx: &LateContext<'_>) {
         }
     }
 }
+
+fn in_macro(span: Span) -> bool {
+    span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
+}
index 23b3ba2296eaf1dedd01840553c754c9fe554c03..fad8fa467d4b80f23917e969d0a865fdde3a6906 100644 (file)
@@ -20,6 +20,7 @@
     ///     main();
     /// }
     /// ```
+    #[clippy::version = "1.38.0"]
     pub MAIN_RECURSION,
     style,
     "recursion using the entrypoint"
index e55aa3f1850fe06a8a6f17b920e6fdcae763267c..ed3166086f7ee05721b5c9e60b7d1109a0850fda 100644 (file)
@@ -26,6 +26,7 @@
     /// let sad_people: Vec<&str> = vec![];
     /// assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
     /// ```
+    #[clippy::version = "1.57.0"]
     pub MANUAL_ASSERT,
     pedantic,
     "`panic!` and only a `panic!` in `if`-then statement"
index b632af455f85545cae35806400df06686c9c9ba9..86819752f90ffe1753eaa2baa4d681f569bf9046 100644 (file)
@@ -30,6 +30,7 @@
     /// ```rust
     /// async fn foo() -> i32 { 42 }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MANUAL_ASYNC_FN,
     style,
     "manual implementations of `async` functions can be simplified using the dedicated syntax"
index 96df3d0a490fa9ce44f7b58199e77f259d2ed8f2..4d8ad566e6b1d84f6131560c29ead691ec55b99b 100644 (file)
@@ -2,7 +2,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher::IfLetOrMatch;
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
-use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
+use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
 use clippy_utils::{
     can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
     peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
@@ -11,7 +11,8 @@
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{
-    def::Res, Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath,
+    def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path,
+    QPath, UnsafeSource,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -36,6 +37,7 @@
     /// ```rust
     /// Some(0).map(|x| x + 1);
     /// ```
+    #[clippy::version = "1.52.0"]
     pub MANUAL_MAP,
     style,
     "reimplementation of `map`"
@@ -92,20 +94,20 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             return;
         }
 
-        let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) {
+        let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) {
             Some(expr) => expr,
             None => return,
         };
 
         // These two lints will go back and forth with each other.
-        if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
+        if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit
             && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
         {
             return;
         }
 
         // `map` won't perform any adjustments.
-        if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
+        if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() {
             return;
         }
 
@@ -119,7 +121,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             None => "",
         };
 
-        match can_move_expr_to_closure(cx, some_expr) {
+        match can_move_expr_to_closure(cx, some_expr.expr) {
             Some(captures) => {
                 // Check if captures the closure will need conflict with borrows made in the scrutinee.
                 // TODO: check all the references made in the scrutinee expression. This will require interacting
@@ -157,12 +159,14 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             };
 
         let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
-            match can_pass_as_func(cx, id, some_expr) {
-                Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
+            if_chain! {
+                if !some_expr.needs_unsafe_block;
+                if let Some(func) = can_pass_as_func(cx, id, some_expr.expr);
+                if func.span.ctxt() == some_expr.expr.span.ctxt();
+                then {
                     snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
-                },
-                _ => {
-                    if path_to_local_id(some_expr, id)
+                } else {
+                    if path_to_local_id(some_expr.expr, id)
                         && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
                         && binding_ref.is_some()
                     {
@@ -175,21 +179,23 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     } else {
                         ""
                     };
-                    format!(
-                        "|{}{}| {}",
-                        annotation,
-                        some_binding,
-                        snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
-                    )
-                },
+                    let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0;
+                    if some_expr.needs_unsafe_block {
+                        format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip)
+                    } else {
+                        format!("|{}{}| {}", annotation, some_binding, expr_snip)
+                    }
+                }
             }
         } else if !is_wild_none && explicit_ref.is_none() {
             // TODO: handle explicit reference annotations.
-            format!(
-                "|{}| {}",
-                snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0,
-                snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
-            )
+            let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0;
+            let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0;
+            if some_expr.needs_unsafe_block {
+                format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip)
+            } else {
+                format!("|{}| {}", pat_snip, expr_snip)
+            }
         } else {
             // Refutable bindings and mixed reference annotations can't be handled by `map`.
             return;
@@ -216,7 +222,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
     match expr.kind {
         ExprKind::Call(func, [arg])
-            if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
+            if path_to_local_id(arg, binding)
+                && cx.typeck_results().expr_adjustments(arg).is_empty()
+                && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) =>
         {
             Some(func)
         },
@@ -236,6 +244,11 @@ enum OptionPat<'a> {
     },
 }
 
+struct SomeExpr<'tcx> {
+    expr: &'tcx Expr<'tcx>,
+    needs_unsafe_block: bool,
+}
+
 // Try to parse into a recognized `Option` pattern.
 // i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
 fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
@@ -256,7 +269,12 @@ fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxC
 }
 
 // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
-fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxContext) -> Option<&'tcx Expr<'tcx>> {
+fn get_some_expr(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    needs_unsafe_block: bool,
+    ctxt: SyntaxContext,
+) -> Option<SomeExpr<'tcx>> {
     // TODO: Allow more complex expressions.
     match expr.kind {
         ExprKind::Call(
@@ -265,15 +283,24 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte
                 ..
             },
             [arg],
-        ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(arg),
+        ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr {
+            expr: arg,
+            needs_unsafe_block,
+        }),
         ExprKind::Block(
             Block {
                 stmts: [],
                 expr: Some(expr),
+                rules,
                 ..
             },
             _,
-        ) => get_some_expr(cx, expr, ctxt),
+        ) => get_some_expr(
+            cx,
+            expr,
+            needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
+            ctxt,
+        ),
         _ => None,
     }
 }
index 335ea001ee476e144a7aa1f5206bb3368f41d796..63a72d4fddeb0b4d30a74d0b394fdde062c509b1 100644 (file)
@@ -52,6 +52,7 @@
     /// #[non_exhaustive]
     /// struct T(pub i32, pub i32);
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MANUAL_NON_EXHAUSTIVE,
     style,
     "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
index cf641d0ce8625765eb398467fa148fddf524a83a..b60e2dc366b416e455ea5fe04ea2a38845f22d89 100644 (file)
@@ -32,6 +32,7 @@
     /// let foo: Option<i32> = None;
     /// foo.ok_or("error");
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MANUAL_OK_OR,
     pedantic,
     "finds patterns that can be encoded more concisely with `Option::ok_or`"
index 4e040508b6bfbdc396c233c1df3222add7802ca1..f8e28f1671f07cc66b26a6f0dd74570d38595d95 100644 (file)
@@ -42,6 +42,7 @@
     ///     assert_eq!(end.to_uppercase(), "WORLD!");
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub MANUAL_STRIP,
     complexity,
     "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
index 42478e3416ece7a2549529503c03943b25fbaadf..aac3c6e0de2bb6b12f2bb0f65f3e7de60a9148f7 100644 (file)
@@ -35,6 +35,7 @@
     /// let foo: Option<i32> = None;
     /// foo.unwrap_or(1);
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MANUAL_UNWRAP_OR,
     complexity,
     "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
index 7db5c7e52ea4565def25026273b4d7e1283b3777..c2b78e21861d7d38b09eb4cf4e12254c58a0ce6d 100644 (file)
@@ -37,6 +37,7 @@
     /// let y = x.iter();
     /// let z = y.cloned();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MAP_CLONE,
     style,
     "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
index 82d3732326ebbf63fa68a171f898cdc37a7d805e..61f21d532c5016ffc3b09a6f7b4705f291c4ab7f 100644 (file)
@@ -97,6 +97,7 @@
     ///         })
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub MAP_ERR_IGNORE,
     restriction,
     "`map_err` should not ignore the original error"
index 40de9ffcd4e253d2c26ef7cc997e0969ec200bc7..58c686d95b3f91caed5e57466b807e33ca9ab8a7 100644 (file)
@@ -47,6 +47,7 @@
     ///     log_err_msg(format_msg(msg));
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OPTION_MAP_UNIT_FN,
     complexity,
     "using `option.map(f)`, where `f` is a function or closure that returns `()`"
@@ -87,6 +88,7 @@
     ///     log_err_msg(format_msg(msg));
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub RESULT_MAP_UNIT_FN,
     complexity,
     "using `result.map(f)`, where `f` is a function or closure that returns `()`"
@@ -188,7 +190,7 @@ fn unit_closure<'tcx>(
 /// Anything else will return `a`.
 fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String {
     match &var_arg.kind {
-        hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace(".", "_"),
+        hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace('.', "_"),
         hir::ExprKind::Path(_) => format!("_{}", snippet(cx, var_arg.span, "")),
         _ => "a".to_string(),
     }
index 552c9a588977fc564d0a409a568839d50d299a56..583b577ffe25d51ea72332b2e130b77c07b7ba20 100644 (file)
@@ -40,6 +40,7 @@
     ///     _ => {},
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MATCH_ON_VEC_ITEMS,
     pedantic,
     "matching on vector elements can panic"
index ecf6ad316a46124a121e0be7de6264ace3ed1098..b1839f00aaee95d1922eea1eaf9c68e3c9a584c2 100644 (file)
@@ -38,6 +38,7 @@
     ///        vec.push(value)
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub MATCH_RESULT_OK,
     style,
     "usage of `ok()` in `let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
index f501593c5187e36e99b823f0a1a6919df425bc86..3316ebf405109591fdcd3270f26b5639887f71aa 100644 (file)
@@ -39,6 +39,7 @@
     ///     _ => {},
     /// }
     /// ```
+    #[clippy::version = "1.58.0"]
     pub MATCH_STR_CASE_MISMATCH,
     correctness,
     "creation of a case altering match expression with non-compliant arms"
index 48e459e016592b49b1a467b87f365f7ca4628d95..eacbfa54cf70f22ad9480dcf367de28233ad7af5 100644 (file)
@@ -8,9 +8,9 @@
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
 use clippy_utils::visitors::is_local_used;
 use clippy_utils::{
-    get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild,
-    meets_msrv, msrvs, path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
-    remove_blocks, strip_pat_refs,
+    get_parent_expr, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs,
+    path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks,
+    strip_pat_refs,
 };
 use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
 use core::iter::{once, ExactSizeIterator};
@@ -25,7 +25,6 @@
 };
 use rustc_hir::{HirIdMap, HirIdSet};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty, TyS, VariantDef};
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -33,8 +32,6 @@
 use rustc_span::sym;
 use std::cmp::Ordering;
 use std::collections::hash_map::Entry;
-use std::iter;
-use std::ops::Bound;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -59,6 +56,7 @@
     ///     bar(foo);
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SINGLE_MATCH,
     style,
     "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
     ///     bar(&other_ref);
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SINGLE_MATCH_ELSE,
     pedantic,
     "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
     ///     _ => frob(x),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_REF_PATS,
     style,
     "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
     ///     bar();
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_BOOL,
     pedantic,
     "a `match` on a boolean expression instead of an `if..else` block"
     ///     _ => (),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_OVERLAPPING_ARM,
     style,
     "a `match` with overlapping arms"
     ///     Err(_) => panic!("err"),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_WILD_ERR_ARM,
     pedantic,
     "a `match` with `Err(_)` arm and take drastic actions"
     /// // Good
     /// let r: Option<&()> = x.as_ref();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_AS_REF,
     complexity,
     "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
     ///     Foo::B(_) => {},
     /// }
     /// ```
+    #[clippy::version = "1.34.0"]
     pub WILDCARD_ENUM_MATCH_ARM,
     restriction,
     "a wildcard enum match arm using `_`"
     ///     Foo::C => {},
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
     pedantic,
     "a wildcard enum match for a single variant"
     ///     _ => {},
     /// }
     /// ```
+    #[clippy::version = "1.42.0"]
     pub WILDCARD_IN_OR_PATTERNS,
     complexity,
     "a wildcard pattern used with others patterns in same match arm"
     /// let wrapper = Wrapper::Data(42);
     /// let Wrapper::Data(data) = wrapper;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INFALLIBLE_DESTRUCTURING_MATCH,
     style,
     "a `match` statement with a single infallible arm instead of a `let`"
     /// // Good
     /// let (c, d) = (a, b);
     /// ```
+    #[clippy::version = "1.43.0"]
     pub MATCH_SINGLE_BINDING,
     complexity,
     "a match with a single binding instead of using `let` statement"
     ///     _ => {},
     /// }
     /// ```
+    #[clippy::version = "1.43.0"]
     pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
     restriction,
     "a match on a struct that binds all fields but still uses the wildcard pattern"
     /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
     /// Ok::<i32, i32>(42).is_ok();
     /// ```
+    #[clippy::version = "1.31.0"]
     pub REDUNDANT_PATTERN_MATCHING,
     style,
     "use the proper utility function avoiding an `if let`"
     /// // Good
     /// let a = matches!(x, Some(0));
     /// ```
+    #[clippy::version = "1.47.0"]
     pub MATCH_LIKE_MATCHES_MACRO,
     style,
     "a match that could be written with the matches! macro"
     ///     Quz => quz(),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_SAME_ARMS,
     pedantic,
     "`match` with identical arm bodies"
@@ -601,7 +614,7 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
 
 impl<'tcx> LateLintPass<'tcx> for Matches {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
+        if expr.span.from_expansion() {
             return;
         }
 
@@ -633,15 +646,11 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let ExprKind::Match(ex, arms, _) = expr.kind {
             check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr);
         }
-        if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
-            check_match_ref_pats(cx, let_expr, once(let_pat), expr);
-        }
     }
 
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
         if_chain! {
-            if !in_external_macro(cx.sess(), local.span);
-            if !in_macro(local.span);
+            if !local.span.from_expansion();
             if let Some(expr) = local.init;
             if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
             if arms.len() == 1 && arms[0].guard.is_none();
@@ -676,8 +685,7 @@ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
 
     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
         if_chain! {
-            if !in_external_macro(cx.sess(), pat.span);
-            if !in_macro(pat.span);
+            if !pat.span.from_expansion();
             if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
             if let Some(def_id) = path.res.opt_def_id();
             let ty = cx.tcx.type_of(def_id);
@@ -704,7 +712,7 @@ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
 #[rustfmt::skip]
 fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
-        if in_macro(expr.span) {
+        if expr.span.from_expansion() {
             // Don't lint match expressions present in
             // macro_rules! block
             return;
@@ -1450,7 +1458,7 @@ fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
 
 #[allow(clippy::too_many_lines)]
 fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
-    if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
+    if expr.span.from_expansion() || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
         return;
     }
 
@@ -1605,27 +1613,27 @@ fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'
     None
 }
 
-/// Gets all arms that are unbounded `PatRange`s.
+/// Gets the ranges for each range pattern arm. Applies `ty` bounds for open ranges.
 fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<FullInt>> {
     arms.iter()
         .filter_map(|arm| {
             if let Arm { pat, guard: None, .. } = *arm {
                 if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
-                    let lhs = match lhs {
+                    let lhs_const = match lhs {
                         Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
                         None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
                     };
-                    let rhs = match rhs {
+                    let rhs_const = match rhs {
                         Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
                         None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
                     };
 
-                    let lhs_val = lhs.int_value(cx, ty)?;
-                    let rhs_val = rhs.int_value(cx, ty)?;
+                    let lhs_val = lhs_const.int_value(cx, ty)?;
+                    let rhs_val = rhs_const.int_value(cx, ty)?;
 
                     let rhs_bound = match range_end {
-                        RangeEnd::Included => Bound::Included(rhs_val),
-                        RangeEnd::Excluded => Bound::Excluded(rhs_val),
+                        RangeEnd::Included => EndBound::Included(rhs_val),
+                        RangeEnd::Excluded => EndBound::Excluded(rhs_val),
                     };
                     return Some(SpannedRange {
                         span: pat.span,
@@ -1637,7 +1645,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
                     let value = constant_full_int(cx, cx.typeck_results(), value)?;
                     return Some(SpannedRange {
                         span: pat.span,
-                        node: (value, Bound::Included(value)),
+                        node: (value, EndBound::Included(value)),
                     });
                 }
             }
@@ -1646,10 +1654,16 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
         .collect()
 }
 
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum EndBound<T> {
+    Included(T),
+    Excluded(T),
+}
+
 #[derive(Debug, Eq, PartialEq)]
-pub struct SpannedRange<T> {
+struct SpannedRange<T> {
     pub span: Span,
-    pub node: (T, Bound<T>),
+    pub node: (T, EndBound<T>),
 }
 
 // Checks if arm has the form `None => None`
@@ -1698,82 +1712,63 @@ fn has_multiple_ref_pats<'a, 'b, I>(pats: I) -> bool
     ref_count > 1
 }
 
-pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
+fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
 where
     T: Copy + Ord,
 {
-    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
-    enum Kind<'a, T> {
-        Start(T, &'a SpannedRange<T>),
-        End(Bound<T>, &'a SpannedRange<T>),
+    #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+    enum BoundKind {
+        EndExcluded,
+        Start,
+        EndIncluded,
     }
 
-    impl<'a, T: Copy> Kind<'a, T> {
-        fn range(&self) -> &'a SpannedRange<T> {
-            match *self {
-                Kind::Start(_, r) | Kind::End(_, r) => r,
-            }
-        }
-
-        fn value(self) -> Bound<T> {
-            match self {
-                Kind::Start(t, _) => Bound::Included(t),
-                Kind::End(t, _) => t,
-            }
-        }
-    }
+    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
+    struct RangeBound<'a, T>(T, BoundKind, &'a SpannedRange<T>);
 
-    impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> {
+    impl<'a, T: Copy + Ord> PartialOrd for RangeBound<'a, T> {
         fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
             Some(self.cmp(other))
         }
     }
 
-    impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
-        fn cmp(&self, other: &Self) -> Ordering {
-            match (self.value(), other.value()) {
-                (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
-                // Range patterns cannot be unbounded (yet)
-                (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
-                (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
-                    Ordering::Equal => Ordering::Greater,
-                    other => other,
-                },
-                (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
-                    Ordering::Equal => Ordering::Less,
-                    other => other,
-                },
-            }
+    impl<'a, T: Copy + Ord> Ord for RangeBound<'a, T> {
+        fn cmp(&self, RangeBound(other_value, other_kind, _): &Self) -> Ordering {
+            let RangeBound(self_value, self_kind, _) = *self;
+            (self_value, self_kind).cmp(&(*other_value, *other_kind))
         }
     }
 
     let mut values = Vec::with_capacity(2 * ranges.len());
 
-    for r in ranges {
-        values.push(Kind::Start(r.node.0, r));
-        values.push(Kind::End(r.node.1, r));
+    for r @ SpannedRange { node: (start, end), .. } in ranges {
+        values.push(RangeBound(*start, BoundKind::Start, r));
+        values.push(match end {
+            EndBound::Excluded(val) => RangeBound(*val, BoundKind::EndExcluded, r),
+            EndBound::Included(val) => RangeBound(*val, BoundKind::EndIncluded, r),
+        });
     }
 
     values.sort();
 
-    for (a, b) in iter::zip(&values, values.iter().skip(1)) {
-        match (a, b) {
-            (&Kind::Start(_, ra), &Kind::End(_, rb)) => {
-                if ra.node != rb.node {
-                    return Some((ra, rb));
-                }
-            },
-            (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
-            _ => {
-                // skip if the range `a` is completely included into the range `b`
-                if let Ordering::Equal | Ordering::Less = a.cmp(b) {
-                    let kind_a = Kind::End(a.range().node.1, a.range());
-                    let kind_b = Kind::End(b.range().node.1, b.range());
-                    if let Ordering::Equal | Ordering::Greater = kind_a.cmp(&kind_b) {
-                        return None;
+    let mut started = vec![];
+
+    for RangeBound(_, kind, range) in values {
+        match kind {
+            BoundKind::Start => started.push(range),
+            BoundKind::EndExcluded | BoundKind::EndIncluded => {
+                let mut overlap = None;
+
+                while let Some(last_started) = started.pop() {
+                    if last_started == range {
+                        break;
                     }
+                    overlap = Some(last_started);
+                }
+
+                if let Some(first_overlapping) = overlap {
+                    return Some((range, first_overlapping));
                 }
-                return Some((a.range(), b.range()));
             },
         }
     }
@@ -1785,7 +1780,8 @@ mod redundant_pattern_match {
     use super::REDUNDANT_PATTERN_MATCHING;
     use clippy_utils::diagnostics::span_lint_and_then;
     use clippy_utils::higher;
-    use clippy_utils::source::{snippet, snippet_with_applicability};
+    use clippy_utils::source::snippet;
+    use clippy_utils::sugg::Sugg;
     use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
     use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
     use if_chain::if_chain;
@@ -1795,7 +1791,7 @@ mod redundant_pattern_match {
     use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
     use rustc_hir::{
         intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
-        Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath,
+        Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath, UnOp,
     };
     use rustc_lint::LateContext;
     use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
@@ -2049,8 +2045,10 @@ fn find_sugg_for_if_let<'tcx>(
 
         let result_expr = match &let_expr.kind {
             ExprKind::AddrOf(_, _, borrowed) => borrowed,
+            ExprKind::Unary(UnOp::Deref, deref) => deref,
             _ => let_expr,
         };
+
         span_lint_and_then(
             cx,
             REDUNDANT_PATTERN_MATCHING,
@@ -2069,12 +2067,15 @@ fn find_sugg_for_if_let<'tcx>(
                 // ^^^^^^^^^^^^^^^^^^^
                 let span = expr_span.until(op_span.shrink_to_hi());
 
-                let mut app = if needs_drop {
+                let app = if needs_drop {
                     Applicability::MaybeIncorrect
                 } else {
                     Applicability::MachineApplicable
                 };
-                let sugg = snippet_with_applicability(cx, op_span, "_", &mut app);
+
+                let sugg = Sugg::hir_with_macro_callsite(cx, result_expr, "_")
+                    .maybe_par()
+                    .to_string();
 
                 diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
 
@@ -2224,29 +2225,29 @@ fn test_overlapping() {
     };
 
     assert_eq!(None, overlapping::<u8>(&[]));
-    assert_eq!(None, overlapping(&[sp(1, Bound::Included(4))]));
+    assert_eq!(None, overlapping(&[sp(1, EndBound::Included(4))]));
     assert_eq!(
         None,
-        overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))])
+        overlapping(&[sp(1, EndBound::Included(4)), sp(5, EndBound::Included(6))])
     );
     assert_eq!(
         None,
         overlapping(&[
-            sp(1, Bound::Included(4)),
-            sp(5, Bound::Included(6)),
-            sp(10, Bound::Included(11))
+            sp(1, EndBound::Included(4)),
+            sp(5, EndBound::Included(6)),
+            sp(10, EndBound::Included(11))
         ],)
     );
     assert_eq!(
-        Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))),
-        overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))])
+        Some((&sp(1, EndBound::Included(4)), &sp(3, EndBound::Included(6)))),
+        overlapping(&[sp(1, EndBound::Included(4)), sp(3, EndBound::Included(6))])
     );
     assert_eq!(
-        Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))),
+        Some((&sp(5, EndBound::Included(6)), &sp(6, EndBound::Included(11)))),
         overlapping(&[
-            sp(1, Bound::Included(4)),
-            sp(5, Bound::Included(6)),
-            sp(6, Bound::Included(11))
+            sp(1, EndBound::Included(4)),
+            sp(5, EndBound::Included(6)),
+            sp(6, EndBound::Included(11))
         ],)
     );
 }
index eb437dc47afb496e63b0bfc37e28a3d209a29f7d..5ffcfd4d2641709c5fa7fd0f8f2fd824e2c5cd24 100644 (file)
@@ -19,6 +19,7 @@
     /// # use std::rc::Rc;
     /// mem::forget(Rc::new(55))
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MEM_FORGET,
     restriction,
     "`mem::forget` usage on `Drop` types, likely to cause memory leaks"
index 1e6057a8fe969eb177c0d3252175799bf3ba8b73..7fc39f17232fd41050e000e3eead23503d34bea9 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::ty::is_non_aggregate_primitive_type;
-use clippy_utils::{in_macro, is_default_equivalent, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths};
+use clippy_utils::{is_default_equivalent, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::OptionNone;
@@ -35,6 +35,7 @@
     /// let mut an_option = Some(0);
     /// let taken = an_option.take();
     /// ```
+    #[clippy::version = "1.31.0"]
     pub MEM_REPLACE_OPTION_WITH_NONE,
     style,
     "replacing an `Option` with `None` instead of `take()`"
@@ -66,6 +67,7 @@
     /// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,
     /// at the cost of either lazily creating a replacement value or aborting
     /// on panic, to ensure that the uninitialized value cannot be observed.
+    #[clippy::version = "1.39.0"]
     pub MEM_REPLACE_WITH_UNINIT,
     correctness,
     "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
@@ -90,6 +92,7 @@
     /// let mut text = String::from("foo");
     /// let taken = std::mem::take(&mut text);
     /// ```
+    #[clippy::version = "1.42.0"]
     pub MEM_REPLACE_WITH_DEFAULT,
     style,
     "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
@@ -213,7 +216,7 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
             expr_span,
             "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
             |diag| {
-                if !in_macro(expr_span) {
+                if !expr_span.from_expansion() {
                     let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
 
                     diag.span_suggestion(
index da428a7b4879b955d4e6937a3dcf4039185f28a6..b7690cf9222cc8be9162ee59b8cff744b686a0e4 100644 (file)
@@ -1,7 +1,7 @@
 use super::{contains_return, BIND_INSTEAD_OF_MAP};
 use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_with_macro_callsite};
-use clippy_utils::{in_macro, remove_blocks, visitors::find_all_ret_expressions};
+use clippy_utils::{remove_blocks, visitors::find_all_ret_expressions};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -106,7 +106,7 @@ fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::
         let mut suggs = Vec::new();
         let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| {
             if_chain! {
-                if !in_macro(ret_expr.span);
+                if !ret_expr.span.from_expansion();
                 if let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind;
                 if let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind;
                 if Self::is_variant(cx, path.res);
index 99c03844f49275e02461ca8a02c0195c81e96772..8ea9312c0f7075557211e94575f30d946a4fe040 100644 (file)
@@ -69,7 +69,7 @@ fn strip_angle_brackets(s: &str) -> Option<&str> {
                         // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>`
                         let ty_str = ty.to_string();
                         let start = ty_str.find('<').unwrap_or(0);
-                        let end = ty_str.find('>').unwrap_or_else(|| ty_str.len());
+                        let end = ty_str.find('>').unwrap_or(ty_str.len());
                         let nb_wildcard = ty_str[start..end].split(',').count();
                         let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1));
                         format!("{}<{}>", elements.join("::"), wildcards)
index dd4ef6e4b58ee72ede7975985c9c76bfa0cb1cec..30d56113c6c102af9e81b9a9c86857bb491406f3 100644 (file)
@@ -9,7 +9,7 @@
 
 use super::ITER_CLONED_COLLECT;
 
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, method_name: &str, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) {
     if_chain! {
         if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec);
         if let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv));
@@ -20,8 +20,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &'
                 cx,
                 ITER_CLONED_COLLECT,
                 to_replace,
-                "called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \
-                more readable",
+                &format!("called `iter().{}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \
+                more readable", method_name),
                 "try",
                 ".to_vec()".to_string(),
                 Applicability::MachineApplicable,
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_split_once.rs b/src/tools/clippy/clippy_lints/src/methods/manual_split_once.rs
deleted file mode 100644 (file)
index 13eb722..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_context;
-use clippy_utils::{is_diag_item_method, match_def_path, paths};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
-use rustc_lint::LateContext;
-use rustc_middle::ty::{self, adjustment::Adjust};
-use rustc_span::{symbol::sym, Span, SyntaxContext};
-
-use super::MANUAL_SPLIT_ONCE;
-
-pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) {
-    if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
-        return;
-    }
-
-    let ctxt = expr.span.ctxt();
-    let (method_name, msg, reverse) = if method_name == "splitn" {
-        ("split_once", "manual implementation of `split_once`", false)
-    } else {
-        ("rsplit_once", "manual implementation of `rsplit_once`", true)
-    };
-    let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) {
-        Some(x) => x,
-        None => return,
-    };
-
-    let mut app = Applicability::MachineApplicable;
-    let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
-    let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
-
-    let sugg = match usage.kind {
-        IterUsageKind::NextTuple => {
-            format!("{}.{}({})", self_snip, method_name, pat_snip)
-        },
-        IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
-        IterUsageKind::Next => {
-            let self_deref = {
-                let adjust = cx.typeck_results().expr_adjustments(self_arg);
-                if adjust.is_empty() {
-                    String::new()
-                } else if cx.typeck_results().expr_ty(self_arg).is_box()
-                    || adjust
-                        .iter()
-                        .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
-                {
-                    format!("&{}", "*".repeat(adjust.len() - 1))
-                } else {
-                    "*".repeat(adjust.len() - 2)
-                }
-            };
-            if usage.unwrap_kind.is_some() {
-                format!(
-                    "{}.{}({}).map_or({}{}, |x| x.0)",
-                    &self_snip, method_name, pat_snip, self_deref, &self_snip
-                )
-            } else {
-                format!(
-                    "Some({}.{}({}).map_or({}{}, |x| x.0))",
-                    &self_snip, method_name, pat_snip, self_deref, &self_snip
-                )
-            }
-        },
-        IterUsageKind::Second => {
-            let access_str = match usage.unwrap_kind {
-                Some(UnwrapKind::Unwrap) => ".unwrap().1",
-                Some(UnwrapKind::QuestionMark) => "?.1",
-                None => ".map(|x| x.1)",
-            };
-            format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str)
-        },
-    };
-
-    span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
-}
-
-enum IterUsageKind {
-    Next,
-    Second,
-    NextTuple,
-    RNextTuple,
-}
-
-enum UnwrapKind {
-    Unwrap,
-    QuestionMark,
-}
-
-struct IterUsage {
-    kind: IterUsageKind,
-    unwrap_kind: Option<UnwrapKind>,
-    span: Span,
-}
-
-#[allow(clippy::too_many_lines)]
-fn parse_iter_usage(
-    cx: &LateContext<'tcx>,
-    ctxt: SyntaxContext,
-    mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
-    reverse: bool,
-) -> Option<IterUsage> {
-    let (kind, span) = match iter.next() {
-        Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
-            let (name, args) = if let ExprKind::MethodCall(name, _, [_, args @ ..], _) = e.kind {
-                (name, args)
-            } else {
-                return None;
-            };
-            let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
-            let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
-
-            match (&*name.ident.as_str(), args) {
-                ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
-                    if reverse {
-                        (IterUsageKind::Second, e.span)
-                    } else {
-                        (IterUsageKind::Next, e.span)
-                    }
-                },
-                ("next_tuple", []) => {
-                    return if_chain! {
-                        if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
-                        if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind();
-                        if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did);
-                        if let ty::Tuple(subs) = subs.type_at(0).kind();
-                        if subs.len() == 2;
-                        then {
-                            Some(IterUsage {
-                                kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple },
-                                span: e.span,
-                                unwrap_kind: None
-                            })
-                        } else {
-                            None
-                        }
-                    };
-                },
-                ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
-                    if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
-                        let span = if name.ident.as_str() == "nth" {
-                            e.span
-                        } else {
-                            if_chain! {
-                                if let Some((_, Node::Expr(next_expr))) = iter.next();
-                                if let ExprKind::MethodCall(next_name, _, [_], _) = next_expr.kind;
-                                if next_name.ident.name == sym::next;
-                                if next_expr.span.ctxt() == ctxt;
-                                if let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id);
-                                if cx.tcx.trait_of_item(next_id) == Some(iter_id);
-                                then {
-                                    next_expr.span
-                                } else {
-                                    return None;
-                                }
-                            }
-                        };
-                        match if reverse { idx ^ 1 } else { idx } {
-                            0 => (IterUsageKind::Next, span),
-                            1 => (IterUsageKind::Second, span),
-                            _ => return None,
-                        }
-                    } else {
-                        return None;
-                    }
-                },
-                _ => return None,
-            }
-        },
-        _ => return None,
-    };
-
-    let (unwrap_kind, span) = if let Some((_, Node::Expr(e))) = iter.next() {
-        match e.kind {
-            ExprKind::Call(
-                Expr {
-                    kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)),
-                    ..
-                },
-                _,
-            ) => {
-                let parent_span = e.span.parent_callsite().unwrap();
-                if parent_span.ctxt() == ctxt {
-                    (Some(UnwrapKind::QuestionMark), parent_span)
-                } else {
-                    (None, span)
-                }
-            },
-            _ if e.span.ctxt() != ctxt => (None, span),
-            ExprKind::MethodCall(name, _, [_], _)
-                if name.ident.name == sym::unwrap
-                    && cx
-                        .typeck_results()
-                        .type_dependent_def_id(e.hir_id)
-                        .map_or(false, |id| is_diag_item_method(cx, id, sym::Option)) =>
-            {
-                (Some(UnwrapKind::Unwrap), e.span)
-            },
-            _ => (None, span),
-        }
-    } else {
-        (None, span)
-    };
-
-    Some(IterUsage {
-        kind,
-        unwrap_kind,
-        span,
-    })
-}
index e46739fea34beb1b70e1f8c0d6a68d5d834bc592..58ec221353567c7421e9794de3f3090c94b4befc 100644 (file)
@@ -33,7 +33,6 @@
 mod iter_skip_next;
 mod iterator_step_by_zero;
 mod manual_saturating_arithmetic;
-mod manual_split_once;
 mod manual_str_repeat;
 mod map_collect_result_unit;
 mod map_flatten;
@@ -50,6 +49,7 @@
 mod single_char_pattern;
 mod single_char_push_string;
 mod skip_while_next;
+mod str_splitn;
 mod string_extend_chars;
 mod suspicious_map;
 mod suspicious_splitn;
@@ -68,7 +68,7 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
-use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
+use clippy_utils::{contains_return, get_trait_def_id, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
@@ -99,6 +99,7 @@
     /// ```rust
     /// [1, 2, 3].iter().copied();
     /// ```
+    #[clippy::version = "1.53.0"]
     pub CLONED_INSTEAD_OF_COPIED,
     pedantic,
     "used `cloned` where `copied` could be used instead"
     /// ```rust
     /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
     /// ```
+    #[clippy::version = "1.53.0"]
     pub FLAT_MAP_OPTION,
     pedantic,
     "used `flat_map` where `filter_map` could be used instead"
     /// // Good
     /// res.expect("more helpful message");
     /// ```
+    #[clippy::version = "1.45.0"]
     pub UNWRAP_USED,
     restriction,
     "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
     /// res?;
     /// # Ok::<(), ()>(())
     /// ```
+    #[clippy::version = "1.45.0"]
     pub EXPECT_USED,
     restriction,
     "using `.expect()` on `Result` or `Option`, which might be better handled"
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHOULD_IMPLEMENT_TRAIT,
     style,
     "defining a method that should be implementing a std trait"
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRONG_SELF_CONVENTION,
     style,
     "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
     /// // Good
     /// x.expect("why did I do this again?");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OK_EXPECT,
     style,
     "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
     /// // Good
     /// x.unwrap_or_default();
     /// ```
+    #[clippy::version = "1.56.0"]
     pub UNWRAP_OR_ELSE_DEFAULT,
     style,
     "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
     /// // Good
     /// x.map_or_else(some_function, |a| a + 1);
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MAP_UNWRAP_OR,
     pedantic,
     "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
     /// // Good
     /// opt.and_then(|a| Some(a + 1));
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OPTION_MAP_OR_NONE,
     style,
     "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
     /// # let r: Result<u32, &str> = Ok(1);
     /// assert_eq!(Some(1), r.ok());
     /// ```
+    #[clippy::version = "1.44.0"]
     pub RESULT_MAP_OR_INTO_OPTION,
     style,
     "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
     /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
     /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
     /// ```
+    #[clippy::version = "1.45.0"]
     pub BIND_INSTEAD_OF_MAP,
     complexity,
     "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
     /// # let vec = vec![1];
     /// vec.iter().find(|x| **x == 0);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FILTER_NEXT,
     complexity,
     "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
     /// # let vec = vec![1];
     /// vec.iter().find(|x| **x != 0);
     /// ```
+    #[clippy::version = "1.42.0"]
     pub SKIP_WHILE_NEXT,
     complexity,
     "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
     /// // Good
     /// vec.iter().flat_map(|x| x.iter());
     /// ```
+    #[clippy::version = "1.31.0"]
     pub MAP_FLATTEN,
-    pedantic,
+    complexity,
     "using combinations of `flatten` and `map` which can usually be written as a single method call"
 }
 
     /// ```rust
     /// (0_i32..10).filter_map(|n| n.checked_add(1));
     /// ```
+    #[clippy::version = "1.51.0"]
     pub MANUAL_FILTER_MAP,
     complexity,
     "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
     /// ```rust
     /// (0_i32..10).find_map(|n| n.checked_add(1));
     /// ```
+    #[clippy::version = "1.51.0"]
     pub MANUAL_FIND_MAP,
     complexity,
     "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
     /// ```rust
     ///  (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
     /// ```
+    #[clippy::version = "1.36.0"]
     pub FILTER_MAP_NEXT,
     pedantic,
     "using combination of `filter_map` and `next` which can usually be written as a single method call"
     /// # let iter = vec![vec![0]].into_iter();
     /// iter.flatten();
     /// ```
+    #[clippy::version = "1.39.0"]
     pub FLAT_MAP_IDENTITY,
     complexity,
     "call to `flat_map` where `flatten` is sufficient"
     ///
     /// let _ = !"hello world".contains("world");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SEARCH_IS_SOME,
     complexity,
     "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
     /// let name = "foo";
     /// if name.starts_with('_') {};
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CHARS_NEXT_CMP,
     style,
     "using `.chars().next()` to check if a string starts with a char"
     /// # let foo = Some(String::new());
     /// foo.unwrap_or_default();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OR_FUN_CALL,
     perf,
     "using any `*or` method with a function call, which suggests `*or_else`"
     /// # let err_msg = "I'm a teapot";
     /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPECT_FUN_CALL,
     perf,
     "using any `expect` method with a function call"
     /// ```rust
     /// 42u64.clone();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CLONE_ON_COPY,
     complexity,
     "using `clone` on a `Copy` type"
     /// // Good
     /// Rc::clone(&x);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CLONE_ON_REF_PTR,
     restriction,
     "using 'clone' on a ref-counted pointer"
     ///     println!("{:p} {:p}", *y, z); // prints out the same pointer
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CLONE_DOUBLE_REF,
     correctness,
     "using `clone` on `&&T`"
     /// // OK, the specialized impl is used
     /// ["foo", "bar"].iter().map(|&s| s.to_string());
     /// ```
+    #[clippy::version = "1.40.0"]
     pub INEFFICIENT_TO_STRING,
     pedantic,
     "using `to_string` on `&&T` where `T: ToString`"
     ///     fn new() -> Self;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEW_RET_NO_SELF,
     style,
     "not returning type containing `Self` in a `new` method"
     ///
     /// // Good
     /// _.split('x');
+    #[clippy::version = "pre 1.29.0"]
     pub SINGLE_CHAR_PATTERN,
     perf,
     "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
     ///     //..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITERATOR_STEP_BY_ZERO,
     correctness,
     "using `Iterator::step_by(0)`, which will panic at runtime"
     /// ```rust
     /// let _ = std::iter::empty::<Option<i32>>().flatten();
     /// ```
+    #[clippy::version = "1.53.0"]
     pub OPTION_FILTER_MAP,
     complexity,
     "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
     /// # s.insert(1);
     /// let x = s.iter().next();
     /// ```
+    #[clippy::version = "1.42.0"]
     pub ITER_NTH_ZERO,
     style,
     "replace `iter.nth(0)` with `iter.next()`"
     /// let bad_vec = some_vec.get(3);
     /// let bad_slice = &some_vec[..].get(3);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_NTH,
     perf,
     "using `.iter().nth()` on a standard library type with O(1) element access"
     /// let bad_vec = some_vec.iter().nth(3);
     /// let bad_slice = &some_vec[..].iter().nth(3);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_SKIP_NEXT,
     style,
     "using `.skip(x).next()` on an iterator"
     /// let last = some_vec[3];
     /// some_vec[0] = 1;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub GET_UNWRAP,
     restriction,
     "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
     /// // Good
     /// a.append(&mut b);
     /// ```
+    #[clippy::version = "1.55.0"]
     pub EXTEND_WITH_DRAIN,
     perf,
     "using vec.append(&mut vec) to move the full range of a vecor to another"
     /// s.push_str(abc);
     /// s.push_str(&def);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_EXTEND_CHARS,
     style,
     "using `x.extend(s.chars())` where s is a `&str` or `String`"
     /// let s = [1, 2, 3, 4, 5];
     /// let s2: Vec<isize> = s.to_vec();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_CLONED_COLLECT,
     style,
     "using `.cloned().collect()` on slice to create a `Vec`"
     /// // Good
     /// name.ends_with('_') || name.ends_with('-');
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CHARS_LAST_CMP,
     style,
     "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
     /// let x: &[i32] = &[1, 2, 3, 4, 5];
     /// do_stuff(x);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_ASREF,
     complexity,
     "using `as_ref` where the types before and after the call are the same"
     /// ```rust
     /// let _ = (0..3).any(|x| x > 2);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_FOLD,
     style,
     "using `fold` when a more succinct alternative exists"
     /// // As there is no conditional check on the argument this could be written as:
     /// let _ = (0..4).map(|x| x + 1);
     /// ```
+    #[clippy::version = "1.31.0"]
     pub UNNECESSARY_FILTER_MAP,
     complexity,
     "using `filter_map` when a more succinct alternative exists"
     /// // Good
     /// let _ = (&vec![3, 4, 5]).iter();
     /// ```
+    #[clippy::version = "1.32.0"]
     pub INTO_ITER_ON_REF,
     style,
     "using `.into_iter()` on a reference"
     /// ```rust
     /// let _ = (0..3).map(|x| x + 2).count();
     /// ```
+    #[clippy::version = "1.39.0"]
     pub SUSPICIOUS_MAP,
     suspicious,
     "suspicious usage of map"
     ///     MaybeUninit::uninit().assume_init()
     /// };
     /// ```
+    #[clippy::version = "1.39.0"]
     pub UNINIT_ASSUMED_INIT,
     correctness,
     "`MaybeUninit::uninit().assume_init()`"
     /// let add = x.saturating_add(y);
     /// let sub = x.saturating_sub(y);
     /// ```
+    #[clippy::version = "1.39.0"]
     pub MANUAL_SATURATING_ARITHMETIC,
     style,
     "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
     /// ```rust
     /// unsafe { (&() as *const ()).offset(1) };
     /// ```
+    #[clippy::version = "1.41.0"]
     pub ZST_OFFSET,
     correctness,
     "Check for offset calculations on raw pointers to zero-sized types"
     /// # Ok::<_, std::io::Error>(())
     /// # };
     /// ```
+    #[clippy::version = "1.42.0"]
     pub FILETYPE_IS_FILE,
     restriction,
     "`FileType::is_file` is not recommended to test for readable file type"
     /// opt.as_deref()
     /// # ;
     /// ```
+    #[clippy::version = "1.42.0"]
     pub OPTION_AS_REF_DEREF,
     complexity,
     "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
     /// a.get(2);
     /// b.get(0);
     /// ```
+    #[clippy::version = "1.46.0"]
     pub ITER_NEXT_SLICE,
     style,
     "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
     /// string.insert(0, 'R');
     /// string.push('R');
     /// ```
+    #[clippy::version = "1.49.0"]
     pub SINGLE_CHAR_ADD_STR,
     style,
     "`push_str()` or `insert_str()` used with a single-character string literal as parameter"
     ///
     /// opt.unwrap_or(42);
     /// ```
+    #[clippy::version = "1.48.0"]
     pub UNNECESSARY_LAZY_EVALUATIONS,
     style,
     "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
     /// ```rust
     /// (0..3).try_for_each(|t| Err(t));
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MAP_COLLECT_RESULT_UNIT,
     style,
     "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
     ///
     /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
     /// ```
+    #[clippy::version = "1.49.0"]
     pub FROM_ITER_INSTEAD_OF_COLLECT,
     pedantic,
     "use `.collect()` instead of `::from_iter()`"
     ///     assert!(x >= 0);
     /// });
     /// ```
+    #[clippy::version = "1.51.0"]
     pub INSPECT_FOR_EACH,
     complexity,
     "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
     /// # let iter = vec![Some(1)].into_iter();
     /// iter.flatten();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub FILTER_MAP_IDENTITY,
     complexity,
     "call to `filter_map` where `flatten` is sufficient"
     /// let x = [1, 2, 3];
     /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub MAP_IDENTITY,
     complexity,
     "using iterator.map(|x| x)"
     /// // Good
     /// let _ = "Hello".as_bytes().get(3);
     /// ```
+    #[clippy::version = "1.52.0"]
     pub BYTES_NTH,
     style,
     "replace `.bytes().nth()` with `.as_bytes().get()`"
     /// let b = a.clone();
     /// let c = a.clone();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub IMPLICIT_CLONE,
     pedantic,
     "implicitly cloning a value by invoking a function on its dereferenced type"
     /// let _ = some_vec.len();
     /// let _ = &some_vec[..].len();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub ITER_COUNT,
     complexity,
     "replace `.iter().count()` with `.len()`"
     ///     // use x
     /// }
     /// ```
+    #[clippy::version = "1.54.0"]
     pub SUSPICIOUS_SPLITN,
     correctness,
     "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
     /// // Good
     /// let x: String = "x".repeat(10);
     /// ```
+    #[clippy::version = "1.54.0"]
     pub MANUAL_STR_REPEAT,
     perf,
     "manual implementation of `str::repeat`"
     /// let (key, value) = _.split_once('=')?;
     /// let value = _.split_once('=')?.1;
     /// ```
+    #[clippy::version = "1.57.0"]
     pub MANUAL_SPLIT_ONCE,
     complexity,
     "replace `.splitn(2, pat)` with `.split_once(pat)`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usages of `str::splitn` (or `str::rsplitn`) where using `str::split` would be the same.
+    /// ### Why is this bad?
+    /// The function `split` is simpler and there is no performance difference in these cases, considering
+    /// that both functions return a lazy iterator.
+    /// ### Example
+    /// ```rust
+    /// // Bad
+    /// let str = "key=value=add";
+    /// let _ = str.splitn(3, '=').next().unwrap();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// // Good
+    /// let str = "key=value=add";
+    /// let _ = str.split('=').next().unwrap();
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub NEEDLESS_SPLITN,
+    complexity,
+    "usages of `str::splitn` that can be replaced with `str::split`"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Option<RustcVersion>,
@@ -1876,7 +1963,8 @@ pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Sel
     SUSPICIOUS_SPLITN,
     MANUAL_STR_REPEAT,
     EXTEND_WITH_DRAIN,
-    MANUAL_SPLIT_ONCE
+    MANUAL_SPLIT_ONCE,
+    NEEDLESS_SPLITN
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -1900,7 +1988,7 @@ macro_rules! method_call {
 
 impl<'tcx> LateLintPass<'tcx> for Methods {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if in_macro(expr.span) {
+        if expr.span.from_expansion() {
             return;
         }
 
@@ -2116,7 +2204,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
             ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
             ("collect", []) => match method_call!(recv) {
-                Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
+                Some((name @ ("cloned" | "copied"), [recv2], _)) => {
+                    iter_cloned_collect::check(cx, name, expr, recv2);
+                },
                 Some(("map", [m_recv, m_arg], _)) => {
                     map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
                 },
@@ -2208,7 +2298,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
                     suspicious_splitn::check(cx, name, expr, recv, count);
                     if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) {
-                        manual_split_once::check(cx, name, expr, recv, pat_arg);
+                        str_splitn::check_manual_split_once(cx, name, expr, recv, pat_arg);
+                    }
+                    if count >= 2 {
+                        str_splitn::check_needless_splitn(cx, name, expr, recv, pat_arg, count);
                     }
                 }
             },
index e99b6b07d1569e88166ba30d44f881071a4984dc..5e5c1038e829e76ddfe9a7007f19ca54328f9412 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_lang_ctor;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_lang_ctor, single_segment_path};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use super::OPTION_MAP_OR_NONE;
 use super::RESULT_MAP_OR_INTO_OPTION;
 
+// The expression inside a closure may or may not have surrounding braces
+// which causes problems when generating a suggestion.
+fn reduce_unit_expression<'a>(
+    cx: &LateContext<'_>,
+    expr: &'a hir::Expr<'_>,
+) -> Option<(&'a hir::Expr<'a>, &'a [hir::Expr<'a>])> {
+    match expr.kind {
+        hir::ExprKind::Call(func, arg_char) => Some((func, arg_char)),
+        hir::ExprKind::Block(block, _) => {
+            match (block.stmts, block.expr) {
+                (&[], Some(inner_expr)) => {
+                    // If block only contains an expression,
+                    // reduce `|x| { x + 1 }` to `|x| x + 1`
+                    reduce_unit_expression(cx, inner_expr)
+                },
+                _ => None,
+            }
+        },
+        _ => None,
+    }
+}
+
 /// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
@@ -31,58 +53,75 @@ pub(super) fn check<'tcx>(
         return;
     }
 
-    let (lint_name, msg, instead, hint) = {
-        let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
-            is_lang_ctor(cx, qpath, OptionNone)
-        } else {
-            return;
-        };
+    let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
+        is_lang_ctor(cx, qpath, OptionNone)
+    } else {
+        return;
+    };
 
-        if !default_arg_is_none {
-            // nothing to lint!
-            return;
-        }
+    if !default_arg_is_none {
+        // nothing to lint!
+        return;
+    }
 
-        let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
-            is_lang_ctor(cx, qpath, OptionSome)
-        } else {
-            false
-        };
+    let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
+        is_lang_ctor(cx, qpath, OptionSome)
+    } else {
+        false
+    };
+
+    if is_option {
+        let self_snippet = snippet(cx, recv.span, "..");
+        if_chain! {
+        if let hir::ExprKind::Closure(_, _, id, span, _) = map_arg.kind;
+            let arg_snippet = snippet(cx, span, "..");
+            let body = cx.tcx.hir().body(id);
+                if let Some((func, arg_char)) = reduce_unit_expression(cx, &body.value);
+                if arg_char.len() == 1;
+                if let hir::ExprKind::Path(ref qpath) = func.kind;
+                if let Some(segment) = single_segment_path(qpath);
+                if segment.ident.name == sym::Some;
+                then {
+                    let func_snippet = snippet(cx, arg_char[0].span, "..");
+                    let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
+                       `map(..)` instead";
+                    return span_lint_and_sugg(
+                        cx,
+                        OPTION_MAP_OR_NONE,
+                        expr.span,
+                        msg,
+                        "try using `map` instead",
+                        format!("{0}.map({1} {2})", self_snippet, arg_snippet,func_snippet),
+                        Applicability::MachineApplicable,
+                    );
+                }
 
-        if is_option {
-            let self_snippet = snippet(cx, recv.span, "..");
-            let func_snippet = snippet(cx, map_arg.span, "..");
-            let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
-                       `and_then(..)` instead";
-            (
-                OPTION_MAP_OR_NONE,
-                msg,
-                "try using `and_then` instead",
-                format!("{0}.and_then({1})", self_snippet, func_snippet),
-            )
-        } else if f_arg_is_some {
-            let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
-                       `ok()` instead";
-            let self_snippet = snippet(cx, recv.span, "..");
-            (
-                RESULT_MAP_OR_INTO_OPTION,
-                msg,
-                "try using `ok` instead",
-                format!("{0}.ok()", self_snippet),
-            )
-        } else {
-            // nothing to lint!
-            return;
         }
-    };
 
-    span_lint_and_sugg(
-        cx,
-        lint_name,
-        expr.span,
-        msg,
-        instead,
-        hint,
-        Applicability::MachineApplicable,
-    );
+        let func_snippet = snippet(cx, map_arg.span, "..");
+        let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
+                       `and_then(..)` instead";
+        return span_lint_and_sugg(
+            cx,
+            OPTION_MAP_OR_NONE,
+            expr.span,
+            msg,
+            "try using `and_then` instead",
+            format!("{0}.and_then({1})", self_snippet, func_snippet),
+            Applicability::MachineApplicable,
+        );
+    } else if f_arg_is_some {
+        let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
+                       `ok()` instead";
+        let self_snippet = snippet(cx, recv.span, "..");
+        return span_lint_and_sugg(
+            cx,
+            RESULT_MAP_OR_INTO_OPTION,
+            expr.span,
+            msg,
+            "try using `ok` instead",
+            format!("{0}.ok()", self_snippet),
+            Applicability::MachineApplicable,
+        );
+    }
 }
index fe9ffde0d337c1f15506e73bdca1e0f373c88da5..4e4653dadcafcdc1bfe926c2c9cda0a58eea697b 100644 (file)
@@ -1,16 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::eager_or_lazy::is_lazyness_candidate;
-use clippy_utils::is_trait_item;
+use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
 use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite};
-use clippy_utils::ty::implements_trait;
-use clippy_utils::ty::{is_type_diagnostic_item, match_type};
-use clippy_utils::{contains_return, last_path_segment, paths};
+use clippy_utils::ty::{implements_trait, match_type};
+use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::{BlockCheckMode, UnsafeSource};
 use rustc_lint::LateContext;
-use rustc_middle::ty;
 use rustc_span::source_map::Span;
 use rustc_span::symbol::{kw, sym};
 use std::borrow::Cow;
@@ -96,25 +92,10 @@ fn check_general_case<'tcx>(
             (&paths::RESULT, true, &["or", "unwrap_or"], "else"),
         ];
 
-        if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &arg.kind {
-            if path.ident.name == sym::len {
-                let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
-
-                match ty.kind() {
-                    ty::Slice(_) | ty::Array(_, _) | ty::Str => return,
-                    _ => (),
-                }
-
-                if is_type_diagnostic_item(cx, ty, sym::Vec) {
-                    return;
-                }
-            }
-        }
-
         if_chain! {
             if KNOW_TYPES.iter().any(|k| k.2.contains(&name));
 
-            if is_lazyness_candidate(cx, arg);
+            if switch_to_lazy_eval(cx, arg);
             if !contains_return(arg);
 
             let self_ty = cx.typeck_results().expr_ty(self_expr);
@@ -166,26 +147,30 @@ fn check_general_case<'tcx>(
         }
     }
 
-    if args.len() == 2 {
-        match args[1].kind {
+    if let [self_arg, arg] = args {
+        let inner_arg = if let hir::ExprKind::Block(
+            hir::Block {
+                stmts: [],
+                expr: Some(expr),
+                ..
+            },
+            _,
+        ) = arg.kind
+        {
+            expr
+        } else {
+            arg
+        };
+        match inner_arg.kind {
             hir::ExprKind::Call(fun, or_args) => {
                 let or_has_args = !or_args.is_empty();
-                if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
+                if !check_unwrap_or_default(cx, name, fun, self_arg, arg, or_has_args, expr.span) {
                     let fun_span = if or_has_args { None } else { Some(fun.span) };
-                    check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span);
+                    check_general_case(cx, name, method_span, self_arg, arg, expr.span, fun_span);
                 }
             },
             hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
-                check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
-            },
-            hir::ExprKind::Block(block, _)
-                if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) =>
-            {
-                if let Some(block_expr) = block.expr {
-                    if let hir::ExprKind::MethodCall(..) = block_expr.kind {
-                        check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
-                    }
-                }
+                check_general_case(cx, name, method_span, self_arg, arg, expr.span, None);
             },
             _ => (),
         }
index 0f2e58d8983faf7188b261d9109cf0a245f4da6a..5ed4ba94884e26b627874b7253608150eca9a0a9 100644 (file)
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::sugg::deref_closure_args;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{is_trait_method, strip_pat_refs};
 use if_chain::if_chain;
@@ -37,6 +38,7 @@ pub(super) fn check<'tcx>(
         if search_snippet.lines().count() <= 1 {
             // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()`
             // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()`
+            let mut applicability = Applicability::MachineApplicable;
             let any_search_snippet = if_chain! {
                 if search_method == "find";
                 if let hir::ExprKind::Closure(_, _, body_id, ..) = search_arg.kind;
@@ -45,9 +47,15 @@ pub(super) fn check<'tcx>(
                 then {
                     if let hir::PatKind::Ref(..) = closure_arg.pat.kind {
                         Some(search_snippet.replacen('&', "", 1))
-                    } else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(closure_arg.pat).kind {
-                        let name = &*ident.name.as_str();
-                        Some(search_snippet.replace(&format!("*{}", name), name))
+                    } else if let PatKind::Binding(..) = strip_pat_refs(closure_arg.pat).kind {
+                        // `find()` provides a reference to the item, but `any` does not,
+                        // so we should fix item usages for suggestion
+                        if let Some(closure_sugg) = deref_closure_args(cx, search_arg) {
+                            applicability = closure_sugg.applicability;
+                            Some(closure_sugg.suggestion)
+                        } else {
+                            Some(search_snippet.to_string())
+                        }
                     } else {
                         None
                     }
@@ -67,7 +75,7 @@ pub(super) fn check<'tcx>(
                         "any({})",
                         any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
                     ),
-                    Applicability::MachineApplicable,
+                    applicability,
                 );
             } else {
                 let iter = snippet(cx, search_recv.span, "..");
@@ -82,7 +90,7 @@ pub(super) fn check<'tcx>(
                         iter,
                         any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
                     ),
-                    Applicability::MachineApplicable,
+                    applicability,
                 );
             }
         } else {
index d313a3db479de6d51c5797985804403272cf32f0..bf9006c690621031f1056c6ee0ac8efb45577d39 100644 (file)
@@ -9,18 +9,21 @@
 
 use super::SINGLE_CHAR_PATTERN;
 
-const PATTERN_METHODS: [(&str, usize); 19] = [
+const PATTERN_METHODS: [(&str, usize); 24] = [
     ("contains", 1),
     ("starts_with", 1),
     ("ends_with", 1),
     ("find", 1),
     ("rfind", 1),
     ("split", 1),
+    ("split_inclusive", 1),
     ("rsplit", 1),
     ("split_terminator", 1),
     ("rsplit_terminator", 1),
     ("splitn", 2),
     ("rsplitn", 2),
+    ("split_once", 1),
+    ("rsplit_once", 1),
     ("matches", 1),
     ("rmatches", 1),
     ("match_indices", 1),
@@ -29,6 +32,8 @@
     ("strip_suffix", 1),
     ("trim_start_matches", 1),
     ("trim_end_matches", 1),
+    ("replace", 1),
+    ("replacen", 1),
 ];
 
 /// lint for length-1 `str`s for methods in `PATTERN_METHODS`
diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
new file mode 100644 (file)
index 0000000..2595f73
--- /dev/null
@@ -0,0 +1,323 @@
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::{is_diag_item_method, match_def_path, paths};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, adjustment::Adjust};
+use rustc_span::{symbol::sym, Span, SyntaxContext};
+
+use super::MANUAL_SPLIT_ONCE;
+
+pub(super) fn check_manual_split_once(
+    cx: &LateContext<'_>,
+    method_name: &str,
+    expr: &Expr<'_>,
+    self_arg: &Expr<'_>,
+    pat_arg: &Expr<'_>,
+) {
+    if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
+        return;
+    }
+
+    let ctxt = expr.span.ctxt();
+    let (method_name, msg, reverse) = if method_name == "splitn" {
+        ("split_once", "manual implementation of `split_once`", false)
+    } else {
+        ("rsplit_once", "manual implementation of `rsplit_once`", true)
+    };
+    let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) {
+        Some(x) => x,
+        None => return,
+    };
+
+    let mut app = Applicability::MachineApplicable;
+    let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
+    let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
+
+    let sugg = match usage.kind {
+        IterUsageKind::NextTuple => {
+            format!("{}.{}({})", self_snip, method_name, pat_snip)
+        },
+        IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
+        IterUsageKind::Next | IterUsageKind::Second => {
+            let self_deref = {
+                let adjust = cx.typeck_results().expr_adjustments(self_arg);
+                if adjust.is_empty() {
+                    String::new()
+                } else if cx.typeck_results().expr_ty(self_arg).is_box()
+                    || adjust
+                        .iter()
+                        .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
+                {
+                    format!("&{}", "*".repeat(adjust.len() - 1))
+                } else {
+                    "*".repeat(adjust.len() - 2)
+                }
+            };
+            if matches!(usage.kind, IterUsageKind::Next) {
+                match usage.unwrap_kind {
+                    Some(UnwrapKind::Unwrap) => {
+                        if reverse {
+                            format!("{}.{}({}).unwrap().0", self_snip, method_name, pat_snip)
+                        } else {
+                            format!(
+                                "{}.{}({}).map_or({}{}, |x| x.0)",
+                                self_snip, method_name, pat_snip, self_deref, &self_snip
+                            )
+                        }
+                    },
+                    Some(UnwrapKind::QuestionMark) => {
+                        format!(
+                            "{}.{}({}).map_or({}{}, |x| x.0)",
+                            self_snip, method_name, pat_snip, self_deref, &self_snip
+                        )
+                    },
+                    None => {
+                        format!(
+                            "Some({}.{}({}).map_or({}{}, |x| x.0))",
+                            &self_snip, method_name, pat_snip, self_deref, &self_snip
+                        )
+                    },
+                }
+            } else {
+                match usage.unwrap_kind {
+                    Some(UnwrapKind::Unwrap) => {
+                        if reverse {
+                            // In this case, no better suggestion is offered.
+                            return;
+                        }
+                        format!("{}.{}({}).unwrap().1", self_snip, method_name, pat_snip)
+                    },
+                    Some(UnwrapKind::QuestionMark) => {
+                        format!("{}.{}({})?.1", self_snip, method_name, pat_snip)
+                    },
+                    None => {
+                        format!("{}.{}({}).map(|x| x.1)", self_snip, method_name, pat_snip)
+                    },
+                }
+            }
+        },
+    };
+
+    span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
+}
+
+enum IterUsageKind {
+    Next,
+    Second,
+    NextTuple,
+    RNextTuple,
+}
+
+enum UnwrapKind {
+    Unwrap,
+    QuestionMark,
+}
+
+struct IterUsage {
+    kind: IterUsageKind,
+    unwrap_kind: Option<UnwrapKind>,
+    span: Span,
+}
+
+#[allow(clippy::too_many_lines)]
+fn parse_iter_usage(
+    cx: &LateContext<'tcx>,
+    ctxt: SyntaxContext,
+    mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
+    reverse: bool,
+) -> Option<IterUsage> {
+    let (kind, span) = match iter.next() {
+        Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
+            let (name, args) = if let ExprKind::MethodCall(name, _, [_, args @ ..], _) = e.kind {
+                (name, args)
+            } else {
+                return None;
+            };
+            let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
+            let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
+
+            match (&*name.ident.as_str(), args) {
+                ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+                    if reverse {
+                        (IterUsageKind::Second, e.span)
+                    } else {
+                        (IterUsageKind::Next, e.span)
+                    }
+                },
+                ("next_tuple", []) => {
+                    return if_chain! {
+                        if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
+                        if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind();
+                        if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did);
+                        if let ty::Tuple(subs) = subs.type_at(0).kind();
+                        if subs.len() == 2;
+                        then {
+                            Some(IterUsage {
+                                kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple },
+                                span: e.span,
+                                unwrap_kind: None
+                            })
+                        } else {
+                            None
+                        }
+                    };
+                },
+                ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+                    if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
+                        let span = if name.ident.as_str() == "nth" {
+                            e.span
+                        } else {
+                            if_chain! {
+                                if let Some((_, Node::Expr(next_expr))) = iter.next();
+                                if let ExprKind::MethodCall(next_name, _, [_], _) = next_expr.kind;
+                                if next_name.ident.name == sym::next;
+                                if next_expr.span.ctxt() == ctxt;
+                                if let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id);
+                                if cx.tcx.trait_of_item(next_id) == Some(iter_id);
+                                then {
+                                    next_expr.span
+                                } else {
+                                    return None;
+                                }
+                            }
+                        };
+                        match if reverse { idx ^ 1 } else { idx } {
+                            0 => (IterUsageKind::Next, span),
+                            1 => (IterUsageKind::Second, span),
+                            _ => return None,
+                        }
+                    } else {
+                        return None;
+                    }
+                },
+                _ => return None,
+            }
+        },
+        _ => return None,
+    };
+
+    let (unwrap_kind, span) = if let Some((_, Node::Expr(e))) = iter.next() {
+        match e.kind {
+            ExprKind::Call(
+                Expr {
+                    kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)),
+                    ..
+                },
+                _,
+            ) => {
+                let parent_span = e.span.parent_callsite().unwrap();
+                if parent_span.ctxt() == ctxt {
+                    (Some(UnwrapKind::QuestionMark), parent_span)
+                } else {
+                    (None, span)
+                }
+            },
+            _ if e.span.ctxt() != ctxt => (None, span),
+            ExprKind::MethodCall(name, _, [_], _)
+                if name.ident.name == sym::unwrap
+                    && cx
+                        .typeck_results()
+                        .type_dependent_def_id(e.hir_id)
+                        .map_or(false, |id| is_diag_item_method(cx, id, sym::Option)) =>
+            {
+                (Some(UnwrapKind::Unwrap), e.span)
+            },
+            _ => (None, span),
+        }
+    } else {
+        (None, span)
+    };
+
+    Some(IterUsage {
+        kind,
+        unwrap_kind,
+        span,
+    })
+}
+
+use super::NEEDLESS_SPLITN;
+
+pub(super) fn check_needless_splitn(
+    cx: &LateContext<'_>,
+    method_name: &str,
+    expr: &Expr<'_>,
+    self_arg: &Expr<'_>,
+    pat_arg: &Expr<'_>,
+    count: u128,
+) {
+    if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
+        return;
+    }
+    let ctxt = expr.span.ctxt();
+    let mut app = Applicability::MachineApplicable;
+    let (reverse, message) = if method_name == "splitn" {
+        (false, "unnecessary use of `splitn`")
+    } else {
+        (true, "unnecessary use of `rsplitn`")
+    };
+    if_chain! {
+        if count >= 2;
+        if check_iter(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), count);
+        then {
+            span_lint_and_sugg(
+                cx,
+                NEEDLESS_SPLITN,
+                expr.span,
+                message,
+                "try this",
+                format!(
+                    "{}.{}({})",
+                    snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0,
+                    if reverse {"rsplit"} else {"split"},
+                    snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0
+                ),
+                app,
+            );
+        }
+    }
+}
+
+fn check_iter(
+    cx: &LateContext<'tcx>,
+    ctxt: SyntaxContext,
+    mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
+    count: u128,
+) -> bool {
+    match iter.next() {
+        Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
+            let (name, args) = if let ExprKind::MethodCall(name, _, [_, args @ ..], _) = e.kind {
+                (name, args)
+            } else {
+                return false;
+            };
+            if_chain! {
+                if let Some(did) = cx.typeck_results().type_dependent_def_id(e.hir_id);
+                if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
+                then {
+                    match (&*name.ident.as_str(), args) {
+                        ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+                            return true;
+                        },
+                        ("next_tuple", []) if count > 2 => {
+                            return true;
+                        },
+                        ("nth", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+                            if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
+                                if count > idx + 1 {
+                                    return true;
+                                }
+                            }
+                        },
+                        _ =>  return false,
+                    }
+                }
+            }
+        },
+        _ => return false,
+    };
+    false
+}
index 740af750b48a7bdaf9fc1a50d47cd1392e21fb1e..1e2765263c87ddb0ed920c8da8a71b74394ee0b3 100644 (file)
@@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(
                 return;
             }
 
-            if eager_or_lazy::is_eagerness_candidate(cx, body_expr) {
+            if eager_or_lazy::switch_to_eager_eval(cx, body_expr) {
                 let msg = if is_option {
                     "unnecessary closure used to substitute value for `Option::None`"
                 } else {
index ba2ce73a1165eeb1ef3707d91b1d373ce78b3fe5..11ad881ee7b95bf1d29b58b89111e85a58b575b7 100644 (file)
@@ -66,7 +66,13 @@ pub(super) fn get_hint_if_single_char_arg(
                 // for regular string: "a"
                 &snip[1..(snip.len() - 1)]
             };
-            let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
+
+            let hint = format!("'{}'", match ch {
+                "'" => "\\'" ,
+                r"\" => "\\\\",
+                _ => ch,
+            });
+
             Some(hint)
         } else {
             None
index dc2dd45e4edb6c1c096b141458f4e5b7e51421d5..a6450aec4f7d46f4e108fbafa953058c741f17f8 100644 (file)
@@ -26,6 +26,7 @@
     /// ```
     /// It will always be equal to `0`. Probably the author meant to clamp the value
     /// between 0 and 100, but has erroneously swapped `min` and `max`.
+    #[clippy::version = "pre 1.29.0"]
     pub MIN_MAX,
     correctness,
     "`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant"
index 78183add9cc7c465b14b369158ae186afa7231dd..2299a09991091a60914bdb32cebe8cf752efeb2e 100644 (file)
@@ -56,6 +56,7 @@
     ///     true
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TOPLEVEL_REF_ARG,
     style,
     "an entire binding declared as `ref`, in a function argument or a `let` statement"
@@ -79,6 +80,7 @@
     /// // Good
     /// if x.is_nan() { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CMP_NAN,
     correctness,
     "comparisons to `NAN`, which will always return false, probably not intended"
     /// if (y - 1.23f64).abs() < error_margin { }
     /// if (y - x).abs() > error_margin { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FLOAT_CMP,
     pedantic,
     "using `==` or `!=` on float values instead of comparing difference with an epsilon"
     /// # let y = String::from("foo");
     /// if x == y {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CMP_OWNED,
     perf,
     "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
     /// let a = x % 1;
     /// let a = x % -1;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MODULO_ONE,
     correctness,
     "taking a number modulo +/-1, which can either panic/overflow or always returns 0"
     /// let y = _x + 1; // Here we are using `_x`, even though it has a leading
     ///                 // underscore. We should rename `_x` to `x`
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USED_UNDERSCORE_BINDING,
     pedantic,
     "using a binding which is prefixed with an underscore"
     /// ```rust,ignore
     /// f() && g(); // We should write `if f() { g(); }`.
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHORT_CIRCUIT_STATEMENT,
     complexity,
     "using a short circuit boolean condition as a statement"
     /// // Good
     /// let a = std::ptr::null::<u32>();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ZERO_PTR,
     style,
     "using `0 as *{const, mut} T`"
     /// // let error_margin = std::f64::EPSILON;
     /// if (x - ONE).abs() < error_margin { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FLOAT_CMP_CONST,
     restriction,
     "using `==` or `!=` on float constants instead of comparing difference with an epsilon"
index 7c3f5f22ade0f9ba447d1dc583791b092f1abf80..6e09e25109fbb31e72fc77d87e5ad488c2fde580 100644 (file)
@@ -46,6 +46,7 @@
     ///     Foo { .. } => {},
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNEEDED_FIELD_PATTERN,
     restriction,
     "struct fields bound to a wildcard instead of using `..`"
@@ -67,6 +68,7 @@
     /// // Good
     /// fn bar(a: i32, _b: i32) {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DUPLICATE_UNDERSCORE_ARGUMENT,
     style,
     "function arguments having names which only differ by an underscore"
@@ -85,6 +87,7 @@
     /// let mut x = 3;
     /// --x;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DOUBLE_NEG,
     style,
     "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
     /// // Good
     /// let y = 0x1A9BACD;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MIXED_CASE_HEX_LITERALS,
     style,
     "hex literals whose letter digits are not consistently upper- or lowercased"
     /// // Good
     /// let y = 123832_i32;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNSEPARATED_LITERAL_SUFFIX,
     restriction,
     "literals whose suffix is not separated by an underscore"
     /// // Good
     /// let y = 123832i32;
     /// ```
+    #[clippy::version = "1.58.0"]
     pub SEPARATED_LITERAL_SUFFIX,
     restriction,
     "literals whose suffix is separated by an underscore"
     /// ```
     ///
     /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
+    #[clippy::version = "pre 1.29.0"]
     pub ZERO_PREFIXED_LITERAL,
     complexity,
     "integer literals starting with `0`"
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BUILTIN_TYPE_SHADOW,
     style,
     "shadowing a builtin type"
     ///     y => (),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub REDUNDANT_PATTERN,
     style,
     "using `name @ _` in a pattern"
     ///     _ => (),
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub UNNEEDED_WILDCARD_PATTERN,
     complexity,
     "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
index 5b2584d43a130db69977205c1af79ebeaea6621c..a8d410508563c72092cfaa587c014863353ba979 100644 (file)
@@ -63,6 +63,7 @@
     /// }
     /// # }
     /// ```
+    #[clippy::version = "1.34.0"]
     pub MISSING_CONST_FOR_FN,
     nursery,
     "Lint functions definitions that could be made `const fn`"
index 564f021268cc8c02eb9da57bf233e922ea6a137a..fc0483a929a7a1e93c3cded4003f6b2835ec2985 100644 (file)
@@ -26,6 +26,7 @@
     /// allowed-by-default lint for
     /// public members, but has no way to enforce documentation of private items.
     /// This lint fixes that.
+    #[clippy::version = "pre 1.29.0"]
     pub MISSING_DOCS_IN_PRIVATE_ITEMS,
     restriction,
     "detects missing documentation for public and private members"
index 448bfc2fdd67c98f33fe60ca83399dfa35b79505..68d49e0f1503116b21341b0e248d0f5e5444b901 100644 (file)
@@ -33,6 +33,7 @@
     /// ```rust,ignore
     /// use serde_json::Value as JsonValue;
     /// ```
+    #[clippy::version = "1.55.0"]
     pub MISSING_ENFORCED_IMPORT_RENAMES,
     restriction,
     "enforce import renames"
index b593c747498e397b76ba9819b3d7388a87f41bd0..ac2f16b49e3f1cd3fc3bc560b8aac36d268735da 100644 (file)
@@ -52,6 +52,7 @@
     ///    fn def_bar() {} // missing #[inline]
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MISSING_INLINE_IN_PUBLIC_ITEMS,
     restriction,
     "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)"
index d41b54745649958de717e61f6d395bfbce0e62e5..3b65f80cba20aeb057459af04949401b6069d318 100644 (file)
@@ -32,6 +32,7 @@
     ///   stuff.rs
     ///   lib.rs
     /// ```
+    #[clippy::version = "1.57.0"]
     pub MOD_MODULE_FILES,
     restriction,
     "checks that module layout is consistent"
@@ -61,6 +62,7 @@
     ///   lib.rs
     /// ```
 
+    #[clippy::version = "1.57.0"]
     pub SELF_NAMED_MODULE_FILES,
     restriction,
     "checks that module layout is consistent"
index f45e68233a1777f5c0052e7b0a0758b024a1c306..d182a7d52497d45fd59cf8d367f8e84e207d3e10 100644 (file)
@@ -24,6 +24,7 @@
     /// ```rust
     /// let x = -17 % 3;
     /// ```
+    #[clippy::version = "1.42.0"]
     pub MODULO_ARITHMETIC,
     restriction,
     "any modulo arithmetic statement"
index 816b2f275fb5e17fb7a701d8867b7bb1f6ab2f32..e45cc86d417ac09aa103c518fabb0ec66f6c9880 100644 (file)
@@ -33,6 +33,7 @@
     /// ctrlc = "=3.1.0"
     /// ansi_term = "=0.11.0"
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MULTIPLE_CRATE_VERSIONS,
     cargo,
     "multiple versions of the same crate being used"
index 8476257f086fb9455c52c08aa62f5be009e00c8e..5fe887a4573cccb99b283dab4af6b5e969e9cc1a 100644 (file)
@@ -72,6 +72,7 @@
     ///     let _: HashSet<Bad> = HashSet::new();
     /// }
     /// ```
+    #[clippy::version = "1.42.0"]
     pub MUTABLE_KEY_TYPE,
     suspicious,
     "Check for mutable `Map`/`Set` key type"
index 7c4cac29ba8e8c699b7a5cfdeea61678f0a477ba..bcbea8f1e66b47edadc368481dc4a50584b48597 100644 (file)
@@ -22,6 +22,7 @@
     /// # let mut y = 1;
     /// let x = &mut &mut y;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUT_MUT,
     pedantic,
     "usage of double-mut refs, e.g., `&mut &mut ...`"
index b96fa4774cbb3633cdcae5b5a74f487ee1b66676..b1e6308d2e1a006ff7d33a7e2ce27c607cf67454 100644 (file)
@@ -38,6 +38,7 @@
     /// let value = value_mutex.get_mut().unwrap();
     /// *value += 1;
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MUT_MUTEX_LOCK,
     style,
     "`&mut Mutex::lock` does unnecessary locking"
index 8d5d7951fc532c6ddd8e42f73ed3d31d814bd257..63a1cf7b7d5bd8492c1fe91da435e106a88d293d 100644 (file)
@@ -23,6 +23,7 @@
     /// // Good
     /// my_vec.push(&value)
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_MUT_PASSED,
     style,
     "an argument passed as a mutable reference although the callee only demands an immutable reference"
index 9b44cf9d43aceafa307ede5bf67de2546569006d..12e219cd5c8714318c166726acae0c659bb6c32c 100644 (file)
@@ -26,6 +26,7 @@
     /// fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }
     /// debug_assert!(take_a_mut_parameter(&mut 5));
     /// ```
+    #[clippy::version = "1.40.0"]
     pub DEBUG_ASSERT_WITH_MUT_CALL,
     nursery,
     "mutable arguments in `debug_assert{,_ne,_eq}!`"
index 5feddcbfc61042c6eeecd73900de7354a172519c..816377fe65e9f31e61b5458784af40267abd46cc 100644 (file)
@@ -36,6 +36,7 @@
     /// # use std::sync::atomic::AtomicBool;
     /// let x = AtomicBool::new(y);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUTEX_ATOMIC,
     perf,
     "using a mutex where an atomic value could be used instead"
@@ -64,6 +65,7 @@
     /// # use std::sync::atomic::AtomicUsize;
     /// let x = AtomicUsize::new(0usize);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUTEX_INTEGER,
     nursery,
     "using a mutex for an integer type"
index 9a3d9383cd98c4255292ae72aa12e2a128274d5e..9838d3cad9f02d537732033a0435ffa73281ea1f 100644 (file)
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use if_chain::if_chain;
 use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
 use rustc_errors::Applicability;
@@ -53,6 +52,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.47.0"]
     pub NEEDLESS_ARBITRARY_SELF_TYPE,
     complexity,
     "type of `self` parameter is already by default `Self`"
@@ -78,7 +78,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
             let self_param = match (binding_mode, mutbl) {
                 (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
                 (Mode::Ref(Some(lifetime)), Mutability::Mut) => {
-                    if in_macro(lifetime.ident.span) {
+                    if lifetime.ident.span.from_expansion() {
                         applicability = Applicability::HasPlaceholders;
                         "&'_ mut self".to_string()
                     } else {
@@ -87,7 +87,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
                 },
                 (Mode::Ref(None), Mutability::Not) => "&self".to_string(),
                 (Mode::Ref(Some(lifetime)), Mutability::Not) => {
-                    if in_macro(lifetime.ident.span) {
+                    if lifetime.ident.span.from_expansion() {
                         applicability = Applicability::HasPlaceholders;
                         "&'_ self".to_string()
                     } else {
@@ -114,7 +114,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
 impl EarlyLintPass for NeedlessArbitrarySelfType {
     fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
         // Bail out if the parameter it's not a receiver or was not written by the user
-        if !p.is_self() || in_macro(p.span) {
+        if !p.is_self() || p.span.from_expansion() {
             return;
         }
 
index 203da29cb917002e3a8e878c035dcf818596c588..a8a8d174a823e309f088517808464b9a6d8ad4d8 100644 (file)
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::in_macro;
 use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -31,6 +30,7 @@
     /// let (x,y) = (true, false);
     /// if x && !y {}
     /// ```
+    #[clippy::version = "1.54.0"]
     pub NEEDLESS_BITWISE_BOOL,
     pedantic,
     "Boolean expressions that use bitwise rather than lazy operators"
@@ -41,7 +41,7 @@
 fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     let ty = cx.typeck_results().expr_ty(expr);
     if_chain! {
-        if !in_macro(expr.span);
+        if !expr.span.from_expansion();
         if let (&ExprKind::Binary(ref op, _, right), &ty::Bool) = (&expr.kind, &ty.kind());
         if op.node == BinOpKind::BitAnd || op.node == BinOpKind::BitOr;
         if let ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) = right.kind;
index 91944653500bbde663f4e14c9226279eca4fcacc..3709f3948c2b5f541295eaeaa074d8595aed9256 100644 (file)
@@ -41,6 +41,7 @@
     /// ```rust,ignore
     /// !x
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_BOOL,
     complexity,
     "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`"
@@ -65,6 +66,7 @@
     /// if x {}
     /// if !y {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BOOL_COMPARISON,
     complexity,
     "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrow.rs b/src/tools/clippy/clippy_lints/src/needless_borrow.rs
deleted file mode 100644 (file)
index f1be90c..0000000
+++ /dev/null
@@ -1,282 +0,0 @@
-//! Checks for needless address of operations (`&`)
-//!
-//! This lint is **warn** by default
-
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{snippet_opt, snippet_with_applicability, snippet_with_context};
-use clippy_utils::{get_parent_expr, in_macro, path_to_local};
-use if_chain::if_chain;
-use rustc_ast::util::parser::PREC_POSTFIX;
-use rustc_data_structures::fx::FxIndexMap;
-use rustc_errors::Applicability;
-use rustc_hir::{BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, UnOp};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_middle::ty::adjustment::{Adjust, Adjustment};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for address of operations (`&`) that are going to
-    /// be dereferenced immediately by the compiler.
-    ///
-    /// ### Why is this bad?
-    /// Suggests that the receiver of the expression borrows
-    /// the expression.
-    ///
-    /// ### Example
-    /// ```rust
-    /// fn fun(_a: &i32) {}
-    ///
-    /// // Bad
-    /// let x: &i32 = &&&&&&5;
-    /// fun(&x);
-    ///
-    /// // Good
-    /// let x: &i32 = &5;
-    /// fun(x);
-    /// ```
-    pub NEEDLESS_BORROW,
-    style,
-    "taking a reference that is going to be automatically dereferenced"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for `ref` bindings which create a reference to a reference.
-    ///
-    /// ### Why is this bad?
-    /// The address-of operator at the use site is clearer about the need for a reference.
-    ///
-    /// ### Example
-    /// ```rust
-    /// // Bad
-    /// let x = Some("");
-    /// if let Some(ref x) = x {
-    ///     // use `x` here
-    /// }
-    ///
-    /// // Good
-    /// let x = Some("");
-    /// if let Some(x) = x {
-    ///     // use `&x` here
-    /// }
-    /// ```
-    pub REF_BINDING_TO_REFERENCE,
-    pedantic,
-    "`ref` binding to a reference"
-}
-
-impl_lint_pass!(NeedlessBorrow => [NEEDLESS_BORROW, REF_BINDING_TO_REFERENCE]);
-#[derive(Default)]
-pub struct NeedlessBorrow {
-    /// The body the first local was found in. Used to emit lints when the traversal of the body has
-    /// been finished. Note we can't lint at the end of every body as they can be nested within each
-    /// other.
-    current_body: Option<BodyId>,
-    /// The list of locals currently being checked by the lint.
-    /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
-    /// This is needed for or patterns where one of the branches can be linted, but another can not
-    /// be.
-    ///
-    /// e.g. `m!(x) | Foo::Bar(ref x)`
-    ref_locals: FxIndexMap<HirId, Option<RefPat>>,
-}
-
-struct RefPat {
-    /// Whether every usage of the binding is dereferenced.
-    always_deref: bool,
-    /// The spans of all the ref bindings for this local.
-    spans: Vec<Span>,
-    /// The applicability of this suggestion.
-    app: Applicability,
-    /// All the replacements which need to be made.
-    replacements: Vec<(Span, String)>,
-}
-
-impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if let Some(local) = path_to_local(e) {
-            self.check_local_usage(cx, e, local);
-        }
-
-        if e.span.from_expansion() {
-            return;
-        }
-        if let ExprKind::AddrOf(BorrowKind::Ref, mutability, inner) = e.kind {
-            if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() {
-                for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) {
-                    if let [
-                        Adjustment {
-                            kind: Adjust::Deref(_), ..
-                        },
-                        Adjustment {
-                            kind: Adjust::Deref(_), ..
-                        },
-                        Adjustment {
-                            kind: Adjust::Borrow(_),
-                            ..
-                        },
-                    ] = *adj3
-                    {
-                        let help_msg_ty = if matches!(mutability, Mutability::Not) {
-                            format!("&{}", ty)
-                        } else {
-                            format!("&mut {}", ty)
-                        };
-
-                        span_lint_and_then(
-                            cx,
-                            NEEDLESS_BORROW,
-                            e.span,
-                            &format!(
-                                "this expression borrows a reference (`{}`) that is immediately dereferenced \
-                             by the compiler",
-                                help_msg_ty
-                            ),
-                            |diag| {
-                                if let Some(snippet) = snippet_opt(cx, inner.span) {
-                                    diag.span_suggestion(
-                                        e.span,
-                                        "change this to",
-                                        snippet,
-                                        Applicability::MachineApplicable,
-                                    );
-                                }
-                            },
-                        );
-                    }
-                }
-            }
-        }
-    }
-
-    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
-        if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind {
-            if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
-                // This binding id has been seen before. Add this pattern to the list of changes.
-                if let Some(prev_pat) = opt_prev_pat {
-                    if in_macro(pat.span) {
-                        // Doesn't match the context of the previous pattern. Can't lint here.
-                        *opt_prev_pat = None;
-                    } else {
-                        prev_pat.spans.push(pat.span);
-                        prev_pat.replacements.push((
-                            pat.span,
-                            snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
-                                .0
-                                .into(),
-                        ));
-                    }
-                }
-                return;
-            }
-
-            if_chain! {
-                if !in_macro(pat.span);
-                if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
-                // only lint immutable refs, because borrowed `&mut T` cannot be moved out
-                if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
-                then {
-                    let mut app = Applicability::MachineApplicable;
-                    let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
-                    self.current_body = self.current_body.or(cx.enclosing_body);
-                    self.ref_locals.insert(
-                        id,
-                        Some(RefPat {
-                            always_deref: true,
-                            spans: vec![pat.span],
-                            app,
-                            replacements: vec![(pat.span, snip.into())],
-                        }),
-                    );
-                }
-            }
-        }
-    }
-
-    fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
-        if Some(body.id()) == self.current_body {
-            for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
-                let replacements = pat.replacements;
-                let app = pat.app;
-                span_lint_and_then(
-                    cx,
-                    if pat.always_deref {
-                        NEEDLESS_BORROW
-                    } else {
-                        REF_BINDING_TO_REFERENCE
-                    },
-                    pat.spans,
-                    "this pattern creates a reference to a reference",
-                    |diag| {
-                        diag.multipart_suggestion("try this", replacements, app);
-                    },
-                );
-            }
-            self.current_body = None;
-        }
-    }
-}
-impl NeedlessBorrow {
-    fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) {
-        if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
-            if let Some(pat) = outer_pat {
-                // Check for auto-deref
-                if !matches!(
-                    cx.typeck_results().expr_adjustments(e),
-                    [
-                        Adjustment {
-                            kind: Adjust::Deref(_),
-                            ..
-                        },
-                        Adjustment {
-                            kind: Adjust::Deref(_),
-                            ..
-                        },
-                        ..
-                    ]
-                ) {
-                    match get_parent_expr(cx, e) {
-                        // Field accesses are the same no matter the number of references.
-                        Some(Expr {
-                            kind: ExprKind::Field(..),
-                            ..
-                        }) => (),
-                        Some(&Expr {
-                            span,
-                            kind: ExprKind::Unary(UnOp::Deref, _),
-                            ..
-                        }) if !in_macro(span) => {
-                            // Remove explicit deref.
-                            let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
-                            pat.replacements.push((span, snip.into()));
-                        },
-                        Some(parent) if !in_macro(parent.span) => {
-                            // Double reference might be needed at this point.
-                            if parent.precedence().order() == PREC_POSTFIX {
-                                // Parentheses would be needed here, don't lint.
-                                *outer_pat = None;
-                            } else {
-                                pat.always_deref = false;
-                                let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
-                                pat.replacements.push((e.span, format!("&{}", snip)));
-                            }
-                        },
-                        _ if !in_macro(e.span) => {
-                            // Double reference might be needed at this point.
-                            pat.always_deref = false;
-                            let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
-                            pat.replacements.push((e.span, format!("&{}", snip)));
-                        },
-                        // Edge case for macros. The span of the identifier will usually match the context of the
-                        // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
-                        // macros
-                        _ => *outer_pat = None,
-                    }
-                }
-            }
-        }
-    }
-}
index 36879eda7c0037733bc940a43f7c9d0546fd98b6..0fcc419e722272b3afccf433f60f3afb07974436 100644 (file)
@@ -38,6 +38,7 @@
     /// let mut v = Vec::<String>::new();
     /// let _ = v.iter_mut().filter(|a| a.is_empty());
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_BORROWED_REFERENCE,
     complexity,
     "destructuring a reference and borrowing the inner value"
index 7aa93ed783920a943ae44caaa23128dbfc4b9990..98a3bce1ff38a1e9d107b54d8f725af240156c9a 100644 (file)
     ///     # break;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_CONTINUE,
     pedantic,
     "`continue` statements that can be replaced by a rearrangement of code"
index 9a6ddc72ce56a04ca17199e8b1baa700763dafcf..0c1da0351739a25de814988d9db6da15b5bb554b 100644 (file)
@@ -40,6 +40,7 @@
     ///     println!("{}", elem);
     /// }
     /// ```
+    #[clippy::version = "1.53.0"]
     pub NEEDLESS_FOR_EACH,
     pedantic,
     "using `for_each` where a `for` loop would be simpler"
diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
new file mode 100644 (file)
index 0000000..e0522f3
--- /dev/null
@@ -0,0 +1,349 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::path_to_local;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::visitors::{expr_visitor, is_local_used};
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for late initializations that can be replaced by a `let` statement
+    /// with an initializer.
+    ///
+    /// ### Why is this bad?
+    /// Assigning in the `let` statement is less repetitive.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let a;
+    /// a = 1;
+    ///
+    /// let b;
+    /// match 3 {
+    ///     0 => b = "zero",
+    ///     1 => b = "one",
+    ///     _ => b = "many",
+    /// }
+    ///
+    /// let c;
+    /// if true {
+    ///     c = 1;
+    /// } else {
+    ///     c = -1;
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let a = 1;
+    ///
+    /// let b = match 3 {
+    ///     0 => "zero",
+    ///     1 => "one",
+    ///     _ => "many",
+    /// };
+    ///
+    /// let c = if true {
+    ///     1
+    /// } else {
+    ///     -1
+    /// };
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub NEEDLESS_LATE_INIT,
+    style,
+    "late initializations that can be replaced by a `let` statement with an initializer"
+}
+declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]);
+
+fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool {
+    let mut seen = false;
+    expr_visitor(cx, |expr| {
+        if let ExprKind::Assign(..) = expr.kind {
+            seen = true;
+        }
+
+        !seen
+    })
+    .visit_stmt(stmt);
+
+    seen
+}
+
+#[derive(Debug)]
+struct LocalAssign {
+    lhs_id: HirId,
+    lhs_span: Span,
+    rhs_span: Span,
+    span: Span,
+}
+
+impl LocalAssign {
+    fn from_expr(expr: &Expr<'_>, span: Span) -> Option<Self> {
+        if let ExprKind::Assign(lhs, rhs, _) = expr.kind {
+            if lhs.span.from_expansion() {
+                return None;
+            }
+
+            Some(Self {
+                lhs_id: path_to_local(lhs)?,
+                lhs_span: lhs.span,
+                rhs_span: rhs.span.source_callsite(),
+                span,
+            })
+        } else {
+            None
+        }
+    }
+
+    fn new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, binding_id: HirId) -> Option<LocalAssign> {
+        let assign = match expr.kind {
+            ExprKind::Block(Block { expr: Some(expr), .. }, _) => Self::from_expr(expr, expr.span),
+            ExprKind::Block(block, _) => {
+                if_chain! {
+                    if let Some((last, other_stmts)) = block.stmts.split_last();
+                    if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = last.kind;
+
+                    let assign = Self::from_expr(expr, last.span)?;
+
+                    // avoid visiting if not needed
+                    if assign.lhs_id == binding_id;
+                    if other_stmts.iter().all(|stmt| !contains_assign_expr(cx, stmt));
+
+                    then {
+                        Some(assign)
+                    } else {
+                        None
+                    }
+                }
+            },
+            ExprKind::Assign(..) => Self::from_expr(expr, expr.span),
+            _ => None,
+        }?;
+
+        if assign.lhs_id == binding_id {
+            Some(assign)
+        } else {
+            None
+        }
+    }
+}
+
+fn assignment_suggestions<'tcx>(
+    cx: &LateContext<'tcx>,
+    binding_id: HirId,
+    exprs: impl IntoIterator<Item = &'tcx Expr<'tcx>>,
+) -> Option<(Applicability, Vec<(Span, String)>)> {
+    let mut assignments = Vec::new();
+
+    for expr in exprs {
+        let ty = cx.typeck_results().expr_ty(expr);
+
+        if ty.is_never() {
+            continue;
+        }
+        if !ty.is_unit() {
+            return None;
+        }
+
+        let assign = LocalAssign::new(cx, expr, binding_id)?;
+
+        assignments.push(assign);
+    }
+
+    let suggestions = assignments
+        .into_iter()
+        .map(|assignment| Some((assignment.span, snippet_opt(cx, assignment.rhs_span)?)))
+        .collect::<Option<Vec<(Span, String)>>>()?;
+
+    let applicability = if suggestions.len() > 1 {
+        // multiple suggestions don't work with rustfix in multipart_suggest
+        // https://github.com/rust-lang/rustfix/issues/141
+        Applicability::Unspecified
+    } else {
+        Applicability::MachineApplicable
+    };
+    Some((applicability, suggestions))
+}
+
+struct Usage<'tcx> {
+    stmt: &'tcx Stmt<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    needs_semi: bool,
+}
+
+fn first_usage<'tcx>(
+    cx: &LateContext<'tcx>,
+    binding_id: HirId,
+    local_stmt_id: HirId,
+    block: &'tcx Block<'tcx>,
+) -> Option<Usage<'tcx>> {
+    block
+        .stmts
+        .iter()
+        .skip_while(|stmt| stmt.hir_id != local_stmt_id)
+        .skip(1)
+        .find(|&stmt| is_local_used(cx, stmt, binding_id))
+        .and_then(|stmt| match stmt.kind {
+            StmtKind::Expr(expr) => Some(Usage {
+                stmt,
+                expr,
+                needs_semi: true,
+            }),
+            StmtKind::Semi(expr) => Some(Usage {
+                stmt,
+                expr,
+                needs_semi: false,
+            }),
+            _ => None,
+        })
+}
+
+fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &Local<'_>) -> Option<String> {
+    let span = local.span.with_hi(match local.ty {
+        // let <pat>: <ty>;
+        // ~~~~~~~~~~~~~~~
+        Some(ty) => ty.span.hi(),
+        // let <pat>;
+        // ~~~~~~~~~
+        None => local.pat.span.hi(),
+    });
+
+    snippet_opt(cx, span)
+}
+
+fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    local: &'tcx Local<'tcx>,
+    local_stmt: &'tcx Stmt<'tcx>,
+    block: &'tcx Block<'tcx>,
+    binding_id: HirId,
+) -> Option<()> {
+    let usage = first_usage(cx, binding_id, local_stmt.hir_id, block)?;
+    let binding_name = cx.tcx.hir().opt_name(binding_id)?;
+    let let_snippet = local_snippet_without_semicolon(cx, local)?;
+
+    match usage.expr.kind {
+        ExprKind::Assign(..) => {
+            let assign = LocalAssign::new(cx, usage.expr, binding_id)?;
+
+            span_lint_and_then(
+                cx,
+                NEEDLESS_LATE_INIT,
+                local_stmt.span,
+                "unneeded late initalization",
+                |diag| {
+                    diag.tool_only_span_suggestion(
+                        local_stmt.span,
+                        "remove the local",
+                        String::new(),
+                        Applicability::MachineApplicable,
+                    );
+
+                    diag.span_suggestion(
+                        assign.lhs_span,
+                        &format!("declare `{}` here", binding_name),
+                        let_snippet,
+                        Applicability::MachineApplicable,
+                    );
+                },
+            );
+        },
+        ExprKind::If(_, then_expr, Some(else_expr)) => {
+            let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?;
+
+            span_lint_and_then(
+                cx,
+                NEEDLESS_LATE_INIT,
+                local_stmt.span,
+                "unneeded late initalization",
+                |diag| {
+                    diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
+
+                    diag.span_suggestion_verbose(
+                        usage.stmt.span.shrink_to_lo(),
+                        &format!("declare `{}` here", binding_name),
+                        format!("{} = ", let_snippet),
+                        applicability,
+                    );
+
+                    diag.multipart_suggestion("remove the assignments from the branches", suggestions, applicability);
+
+                    if usage.needs_semi {
+                        diag.span_suggestion(
+                            usage.stmt.span.shrink_to_hi(),
+                            "add a semicolon after the `if` expression",
+                            ";".to_string(),
+                            applicability,
+                        );
+                    }
+                },
+            );
+        },
+        ExprKind::Match(_, arms, MatchSource::Normal) => {
+            let (applicability, suggestions) = assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?;
+
+            span_lint_and_then(
+                cx,
+                NEEDLESS_LATE_INIT,
+                local_stmt.span,
+                "unneeded late initalization",
+                |diag| {
+                    diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
+
+                    diag.span_suggestion_verbose(
+                        usage.stmt.span.shrink_to_lo(),
+                        &format!("declare `{}` here", binding_name),
+                        format!("{} = ", let_snippet),
+                        applicability,
+                    );
+
+                    diag.multipart_suggestion(
+                        "remove the assignments from the `match` arms",
+                        suggestions,
+                        applicability,
+                    );
+
+                    if usage.needs_semi {
+                        diag.span_suggestion(
+                            usage.stmt.span.shrink_to_hi(),
+                            "add a semicolon after the `match` expression",
+                            ";".to_string(),
+                            applicability,
+                        );
+                    }
+                },
+            );
+        },
+        _ => {},
+    };
+
+    Some(())
+}
+
+impl LateLintPass<'tcx> for NeedlessLateInit {
+    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+        let mut parents = cx.tcx.hir().parent_iter(local.hir_id);
+
+        if_chain! {
+            if let Local {
+                init: None,
+                pat: &Pat {
+                    kind: PatKind::Binding(_, binding_id, _, None),
+                    ..
+                },
+                source: LocalSource::Normal,
+                ..
+            } = local;
+            if let Some((_, Node::Stmt(local_stmt))) = parents.next();
+            if let Some((_, Node::Block(block))) = parents.next();
+
+            then {
+                check(cx, local, local_stmt, block, binding_id);
+            }
+        }
+    }
+}
index fbdaaf51f7484668306874e2f91f15d4087a7981..a28b08c33ec46c533d4d4cfa361d80acd9b3c099 100644 (file)
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
@@ -27,6 +26,7 @@
     /// let a = Some(&1);
     /// let b = a;
     /// ```
+    #[clippy::version = "1.57.0"]
     pub NEEDLESS_OPTION_AS_DEREF,
     complexity,
     "no-op use of `deref` or `deref_mut` method to `Option`."
@@ -38,7 +38,7 @@
 
 impl<'tcx> LateLintPass<'tcx> for OptionNeedlessDeref {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if expr.span.from_expansion() || in_macro(expr.span) {
+        if expr.span.from_expansion() {
             return;
         }
         let typeck = cx.typeck_results();
index 352dc6f8bec3dd7d07c621d211837dc870ae46d1..35877d51c0c166c89ec0e70598ed377a84b70a96 100644 (file)
@@ -51,6 +51,7 @@
     ///     assert_eq!(v.len(), 42);
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_PASS_BY_VALUE,
     pedantic,
     "functions taking arguments by value, but not consuming them in its body"
index 42e48336e1539595d7f877698bd2f42031a83507..1ffed6a052499119e5bda1293034958b4dce05b8 100644 (file)
@@ -53,6 +53,7 @@
     ///     tr.and_then(|t| t.magic)
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub NEEDLESS_QUESTION_MARK,
     complexity,
     "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
@@ -92,10 +93,16 @@ fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
 }
 
 fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
-    let inner_expr = if_chain! {
+    if_chain! {
         if let ExprKind::Call(path, [arg]) = &expr.kind;
         if let ExprKind::Path(ref qpath) = &path.kind;
-        if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
+        let sugg_remove = if is_lang_ctor(cx, qpath, OptionSome) {
+            "Some()"
+        } else if is_lang_ctor(cx, qpath, ResultOk) {
+            "Ok()"
+        } else {
+            return;
+        };
         if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &arg.kind;
         if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind;
         if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)) = &called.kind;
@@ -103,15 +110,16 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
         let expr_ty = cx.typeck_results().expr_ty(expr);
         let inner_ty = cx.typeck_results().expr_ty(inner_expr);
         if TyS::same_type(expr_ty, inner_ty);
-        then { inner_expr } else { return; }
-    };
-    span_lint_and_sugg(
-        cx,
-        NEEDLESS_QUESTION_MARK,
-        expr.span,
-        "question mark operator is useless here",
-        "try",
-        format!("{}", snippet(cx, inner_expr.span, r#""...""#)),
-        Applicability::MachineApplicable,
-    );
+        then {
+            span_lint_and_sugg(
+                cx,
+                NEEDLESS_QUESTION_MARK,
+                expr.span,
+                "question mark operator is useless here",
+                &format!("try removing question mark and `{}`", sugg_remove),
+                format!("{}", snippet(cx, inner_expr.span, r#""...""#)),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
 }
index 2a33b7392caa478d08c35b7de0d17c0cf7bb6835..ed315efaa2fa7c546de3a7fabce42dc2997a80f5 100644 (file)
@@ -40,6 +40,7 @@
     ///     ..zero_point
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_UPDATE,
     complexity,
     "using `Foo { ..base }` when there are no missing fields"
index 6ad49b7060565ce9b8cea8db5b88abff1dc76825..efe31a1544187ebeec7ca98fb3e6e034272f9312 100644 (file)
@@ -36,6 +36,7 @@
     ///     _ => false,
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEG_CMP_OP_ON_PARTIAL_ORD,
     complexity,
     "The use of negated comparison operators on partially ordered types may produce confusing code."
index 1b15d29439f780cdee65e26b1dcf002c12aea846..cb67fab17400590a04c008afd701f66047f64d6a 100644 (file)
@@ -20,6 +20,7 @@
     /// ```ignore
     /// x * -1
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEG_MULTIPLY,
     style,
     "multiplying integers with `-1`"
index 0ac27f1cba2262ce1a8c1423678ac787b67d966e..f0c0c89ca8f3b5bfff8df170daa76273a47b3566 100644 (file)
@@ -45,6 +45,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEW_WITHOUT_DEFAULT,
     style,
     "`fn new() -> Self` method without `Default` implementation"
index 6dae8f320436fc49609aa428cc808a218f84edd7..6fcc9ca29b923b166bf7009a4d57b1725473b388 100644 (file)
@@ -22,6 +22,7 @@
     /// ```rust
     /// 0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NO_EFFECT,
     complexity,
     "statements with no effect"
@@ -44,6 +45,7 @@
     /// ```rust,ignore
     /// let _i_serve_no_purpose = 1;
     /// ```
+    #[clippy::version = "1.58.0"]
     pub NO_EFFECT_UNDERSCORE_BINDING,
     pedantic,
     "binding to `_` prefixed variable with no side-effect"
@@ -62,6 +64,7 @@
     /// ```rust,ignore
     /// compute_array()[0];
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_OPERATION,
     complexity,
     "outer expressions with no effect"
@@ -130,9 +133,12 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
         },
         ExprKind::Call(callee, args) => {
             if let ExprKind::Path(ref qpath) = callee.kind {
-                let res = cx.qpath_res(qpath, callee.hir_id);
+                if cx.typeck_results().type_dependent_def(expr.hir_id).is_some() {
+                    // type-dependent function call like `impl FnOnce for X`
+                    return false;
+                }
                 let def_matched = matches!(
-                    res,
+                    cx.qpath_res(qpath, callee.hir_id),
                     Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
                 );
                 if def_matched || is_range_literal(expr) {
@@ -158,12 +164,13 @@ fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
         if !&reduced.iter().any(|e| e.span.from_expansion());
         then {
             if let ExprKind::Index(..) = &expr.kind {
-                let snippet;
-                if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) {
-                    snippet = format!("assert!({}.len() > {});", &arr, &func);
+                let snippet = if let (Some(arr), Some(func)) =
+                    (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span))
+                {
+                    format!("assert!({}.len() > {});", &arr, &func)
                 } else {
                     return;
-                }
+                };
                 span_lint_hir_and_then(
                     cx,
                     UNNECESSARY_OPERATION,
@@ -235,6 +242,10 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
         },
         ExprKind::Call(callee, args) => {
             if let ExprKind::Path(ref qpath) = callee.kind {
+                if cx.typeck_results().type_dependent_def(expr.hir_id).is_some() {
+                    // type-dependent function call like `impl FnOnce for X`
+                    return None;
+                }
                 let res = cx.qpath_res(qpath, callee.hir_id);
                 match res {
                     Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
index 2ea97eb88f783558076b8655f62b0444723d4701..074ba9e92ba4dfcea5e8921afbe8139302efe7f1 100644 (file)
@@ -69,6 +69,7 @@
     /// STATIC_ATOM.store(9, SeqCst);
     /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DECLARE_INTERIOR_MUTABLE_CONST,
     style,
     "declaring `const` with interior mutability"
     /// STATIC_ATOM.store(9, SeqCst);
     /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BORROW_INTERIOR_MUTABLE_CONST,
     style,
     "referencing `const` with interior mutability"
index e28cc49bf2a1a0f955ac3686c132d4c201b2b6ef..5559fac0a8a1e7954e45d84e2809864ccd701f9f 100644 (file)
@@ -24,6 +24,7 @@
     /// let checked_exp = something;
     /// let checked_expr = something_else;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SIMILAR_NAMES,
     pedantic,
     "similarly named items and bindings"
@@ -42,6 +43,7 @@
     /// ```ignore
     /// let (a, b, c, d, e, f, g) = (...);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MANY_SINGLE_CHAR_NAMES,
     pedantic,
     "too many single character bindings"
@@ -62,6 +64,7 @@
     /// let ___1 = 1;
     /// let __1___2 = 11;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub JUST_UNDERSCORES_AND_DIGITS,
     style,
     "unclear name"
@@ -357,7 +360,12 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
             return;
         }
 
-        if let ItemKind::Fn(box ast::Fn { ref sig, body: Some(ref blk), .. }) = item.kind {
+        if let ItemKind::Fn(box ast::Fn {
+            ref sig,
+            body: Some(ref blk),
+            ..
+        }) = item.kind
+        {
             do_check(self, cx, &item.attrs, &sig.decl, blk);
         }
     }
@@ -367,7 +375,12 @@ fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
             return;
         }
 
-        if let AssocItemKind::Fn(box ast::Fn { ref sig, body: Some(ref blk), .. }) = item.kind {
+        if let AssocItemKind::Fn(box ast::Fn {
+            ref sig,
+            body: Some(ref blk),
+            ..
+        }) = item.kind
+        {
             do_check(self, cx, &item.attrs, &sig.decl, blk);
         }
     }
index 3b74f69d3753abe38644a6dfe1b618f22b86f1fd..4b57dbc4c412a0d117352899f7ec55005cb19184 100644 (file)
@@ -32,6 +32,7 @@
     /// let mut options = OpenOptions::new();
     /// options.mode(0o644);
     /// ```
+    #[clippy::version = "1.53.0"]
     pub NON_OCTAL_UNIX_PERMISSIONS,
     correctness,
     "use of non-octal value to set unix file permissions, which will be translated into octal"
index 7ebf84d400f569c3f891bc7d417afeeb2d741167..bba542ce8ca7b408a5050963d634052eca559546 100644 (file)
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::is_lint_allowed;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::{implements_trait, is_copy};
+use clippy_utils::{is_lint_allowed, match_def_path, paths};
 use rustc_ast::ImplPolarity;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{FieldDef, Item, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::sym;
@@ -43,6 +44,7 @@
     /// ```
     /// Use thread-safe types like [`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
     /// or specify correct bounds on generic type parameters (`T: Send`).
+    #[clippy::version = "1.57.0"]
     pub NON_SEND_FIELDS_IN_SEND_TY,
     suspicious,
     "there is field that does not implement `Send` in a `Send` struct"
@@ -76,6 +78,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         // single `AdtDef` may have multiple `Send` impls due to generic
         // parameters, and the lint is much easier to implement in this way.
         if_chain! {
+            if !in_external_macro(cx.tcx.sess, item.span);
             if let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send);
             if let ItemKind::Impl(hir_impl) = &item.kind;
             if let Some(trait_ref) = &hir_impl.of_trait;
@@ -180,7 +183,7 @@ fn ty_allowed_without_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty
         return true;
     }
 
-    if is_copy(cx, ty) && !contains_raw_pointer(cx, ty) {
+    if is_copy(cx, ty) && !contains_pointer_like(cx, ty) {
         return true;
     }
 
@@ -200,7 +203,7 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t
             .all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)),
         ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait),
         ty::Adt(_, substs) => {
-            if contains_raw_pointer(cx, ty) {
+            if contains_pointer_like(cx, ty) {
                 // descends only if ADT contains any raw pointers
                 substs.iter().all(|generic_arg| match generic_arg.unpack() {
                     GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait),
@@ -217,14 +220,20 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t
     }
 }
 
-/// Checks if the type contains any raw pointers in substs (including nested ones).
-fn contains_raw_pointer<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool {
+/// Checks if the type contains any pointer-like types in substs (including nested ones)
+fn contains_pointer_like<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool {
     for ty_node in target_ty.walk(cx.tcx) {
-        if_chain! {
-            if let GenericArgKind::Type(inner_ty) = ty_node.unpack();
-            if let ty::RawPtr(_) = inner_ty.kind();
-            then {
-                return true;
+        if let GenericArgKind::Type(inner_ty) = ty_node.unpack() {
+            match inner_ty.kind() {
+                ty::RawPtr(_) => {
+                    return true;
+                },
+                ty::Adt(adt_def, _) => {
+                    if match_def_path(cx, adt_def.did, &paths::PTR_NON_NULL) {
+                        return true;
+                    }
+                },
+                _ => (),
             }
         }
     }
index ca660a9250db15f2332a71b44dfad2340c8d1f06..a04d589f880fa9aca9e43677f0076129bc9cdd69 100644 (file)
@@ -3,14 +3,16 @@
     hash::{Hash, Hasher},
 };
 
-use clippy_utils::{diagnostics::span_lint_and_help, in_macro, is_direct_expn_of, source::snippet_opt};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::def_id::DefId;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
+use rustc_span::hygiene::{ExpnKind, MacroKind};
+use rustc_span::{Span, Symbol};
 use serde::{de, Deserialize};
 
 declare_clippy_lint! {
@@ -29,6 +31,7 @@
     /// ```rust
     /// vec![1, 2, 3];
     /// ```
+    #[clippy::version = "1.55.0"]
     pub NONSTANDARD_MACRO_BRACES,
     nursery,
     "check consistent use of braces in macro"
@@ -37,7 +40,7 @@
 const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")];
 
 /// The (name, (open brace, close brace), source snippet)
-type MacroInfo<'a> = (&'a str, &'a (String, String), String);
+type MacroInfo<'a> = (Symbol, &'a (String, String), String);
 
 #[derive(Clone, Debug, Default)]
 pub struct MacroBraces {
@@ -93,35 +96,34 @@ fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
 
 fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a MacroBraces) -> Option<MacroInfo<'a>> {
     let unnested_or_local = || {
-        let nested = in_macro(span.ctxt().outer_expn_data().call_site);
-        !nested
+        !span.ctxt().outer_expn_data().call_site.from_expansion()
             || span
                 .macro_backtrace()
                 .last()
                 .map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local))
     };
     if_chain! {
-        // Make sure we are only one level deep otherwise there are to many FP's
-        if in_macro(span);
-        if let Some((name, braces)) = find_matching_macro(span, &mac_braces.macro_braces);
+        if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind;
+        let name = &*mac_name.as_str();
+        if let Some(braces) = mac_braces.macro_braces.get(name);
         if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site);
         // we must check only invocation sites
         // https://github.com/rust-lang/rust-clippy/issues/7422
         if snip.starts_with(&format!("{}!", name));
         if unnested_or_local();
         // make formatting consistent
-        let c = snip.replace(" ", "");
+        let c = snip.replace(' ', "");
         if !c.starts_with(&format!("{}!{}", name, braces.0));
         if !mac_braces.done.contains(&span.ctxt().outer_expn_data().call_site);
         then {
-            Some((name, braces, snip))
+            Some((mac_name, braces, snip))
         } else {
             None
         }
     }
 }
 
-fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: &str, span: Span) {
+fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: Symbol, span: Span) {
     let with_space = &format!("! {}", braces.0);
     let without_space = &format!("!{}", braces.0);
     let mut help = snip;
@@ -144,15 +146,6 @@ fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), nam
     );
 }
 
-fn find_matching_macro(
-    span: Span,
-    braces: &FxHashMap<String, (String, String)>,
-) -> Option<(&String, &(String, String))> {
-    braces
-        .iter()
-        .find(|(macro_name, _)| is_direct_expn_of(span, macro_name).is_some())
-}
-
 fn macro_braces(conf: FxHashSet<MacroMatcher>) -> FxHashMap<String, (String, String)> {
     let mut braces = vec![
         macro_matcher!(
diff --git a/src/tools/clippy/clippy_lints/src/octal_escapes.rs b/src/tools/clippy/clippy_lints/src/octal_escapes.rs
new file mode 100644 (file)
index 0000000..9c97143
--- /dev/null
@@ -0,0 +1,150 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_ast::ast::{Expr, ExprKind};
+use rustc_ast::token::{Lit, LitKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+use std::fmt::Write;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `\0` escapes in string and byte literals that look like octal
+    /// character escapes in C.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// C and other languages support octal character escapes in strings, where
+    /// a backslash is followed by up to three octal digits. For example, `\033`
+    /// stands for the ASCII character 27 (ESC). Rust does not support this
+    /// notation, but has the escape code `\0` which stands for a null
+    /// byte/character, and any following digits do not form part of the escape
+    /// sequence. Therefore, `\033` is not a compiler error but the result may
+    /// be surprising.
+    ///
+    /// ### Known problems
+    /// The actual meaning can be the intended one. `\x00` can be used in these
+    /// cases to be unambigious.
+    ///
+    /// The lint does not trigger for format strings in `print!()`, `write!()`
+    /// and friends since the string is already preprocessed when Clippy lints
+    /// can see it.
+    ///
+    /// # Example
+    /// ```rust
+    /// // Bad
+    /// let one = "\033[1m Bold? \033[0m";  // \033 intended as escape
+    /// let two = "\033\0";                 // \033 intended as null-3-3
+    ///
+    /// // Good
+    /// let one = "\x1b[1mWill this be bold?\x1b[0m";
+    /// let two = "\x0033\x00";
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub OCTAL_ESCAPES,
+    suspicious,
+    "string escape sequences looking like octal characters"
+}
+
+declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]);
+
+impl EarlyLintPass for OctalEscapes {
+    fn check_expr(&mut self, cx: &EarlyContext<'tcx>, expr: &Expr) {
+        if in_external_macro(cx.sess, expr.span) {
+            return;
+        }
+
+        if let ExprKind::Lit(lit) = &expr.kind {
+            if matches!(lit.token.kind, LitKind::Str) {
+                check_lit(cx, &lit.token, lit.span, true);
+            } else if matches!(lit.token.kind, LitKind::ByteStr) {
+                check_lit(cx, &lit.token, lit.span, false);
+            }
+        }
+    }
+}
+
+fn check_lit(cx: &EarlyContext<'tcx>, lit: &Lit, span: Span, is_string: bool) {
+    let contents = lit.symbol.as_str();
+    let mut iter = contents.char_indices().peekable();
+    let mut found = vec![];
+
+    // go through the string, looking for \0[0-7][0-7]?
+    while let Some((from, ch)) = iter.next() {
+        if ch == '\\' {
+            if let Some((_, '0')) = iter.next() {
+                // collect up to two further octal digits
+                if let Some((mut to, '0'..='7')) = iter.next() {
+                    if let Some((_, '0'..='7')) = iter.peek() {
+                        to += 1;
+                    }
+                    found.push((from, to + 1));
+                }
+            }
+        }
+    }
+
+    if found.is_empty() {
+        return;
+    }
+
+    // construct two suggestion strings, one with \x escapes with octal meaning
+    // as in C, and one with \x00 for null bytes.
+    let mut suggest_1 = if is_string { "\"" } else { "b\"" }.to_string();
+    let mut suggest_2 = suggest_1.clone();
+    let mut index = 0;
+    for (from, to) in found {
+        suggest_1.push_str(&contents[index..from]);
+        suggest_2.push_str(&contents[index..from]);
+
+        // construct a replacement escape
+        // the maximum value is \077, or \x3f, so u8 is sufficient here
+        if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) {
+            write!(&mut suggest_1, "\\x{:02x}", n).unwrap();
+        }
+
+        // append the null byte as \x00 and the following digits literally
+        suggest_2.push_str("\\x00");
+        suggest_2.push_str(&contents[from + 2..to]);
+
+        index = to;
+    }
+    suggest_1.push_str(&contents[index..]);
+    suggest_1.push('"');
+    suggest_2.push_str(&contents[index..]);
+    suggest_2.push('"');
+
+    span_lint_and_then(
+        cx,
+        OCTAL_ESCAPES,
+        span,
+        &format!(
+            "octal-looking escape in {} literal",
+            if is_string { "string" } else { "byte string" }
+        ),
+        |diag| {
+            diag.help(&format!(
+                "octal escapes are not supported, `\\0` is always a null {}",
+                if is_string { "character" } else { "byte" }
+            ));
+            // suggestion 1: equivalent hex escape
+            diag.span_suggestion(
+                span,
+                "if an octal escape was intended, use the hexadecimal representation instead",
+                suggest_1,
+                Applicability::MaybeIncorrect,
+            );
+            // suggestion 2: unambiguous null byte
+            diag.span_suggestion(
+                span,
+                &format!(
+                    "if the null {} is intended, disambiguate using",
+                    if is_string { "character" } else { "byte" }
+                ),
+                suggest_2,
+                Applicability::MaybeIncorrect,
+            );
+        },
+    );
+}
index 5752342cf623b67e55292b5079b2c89ab43fa8cb..2c77100bdcfc8837a52b2751fb519a8e674d8bf1 100644 (file)
@@ -22,6 +22,7 @@
     ///
     /// OpenOptions::new().read(true).truncate(true);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NONSENSICAL_OPEN_OPTIONS,
     correctness,
     "nonsensical combination of options for opening a file"
index d7306628030f6ddc3d02a7a134e7ac01959d2f16..3f5286ba097b5970993d6f7027b94c512a556041 100644 (file)
@@ -26,6 +26,7 @@
     /// ```rust,no_run
     /// let _ = env!("HOME");
     /// ```
+    #[clippy::version = "1.43.0"]
     pub OPTION_ENV_UNWRAP,
     correctness,
     "using `option_env!(...).unwrap()` to get environment variable"
index cbe1c5d44d513044d745718518c69876f69a10f3..262be17f61751d495d862f8d628e13c0a0c1ae79 100644 (file)
@@ -3,7 +3,7 @@
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{
-    can_move_expr_to_closure, eager_or_lazy, in_constant, in_macro, is_else_clause, is_lang_ctor, peel_hir_expr_while,
+    can_move_expr_to_closure, eager_or_lazy, in_constant, is_else_clause, is_lang_ctor, peel_hir_expr_while,
     CaptureKind,
 };
 use if_chain::if_chain;
@@ -59,6 +59,7 @@
     ///     y*y
     /// }, |foo| foo);
     /// ```
+    #[clippy::version = "1.47.0"]
     pub OPTION_IF_LET_ELSE,
     nursery,
     "reimplementation of Option::map_or"
@@ -110,7 +111,7 @@ fn extract_body_from_expr<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
 fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
     format!(
         "{}{}",
-        Sugg::hir(cx, cond_expr, "..").maybe_par(),
+        Sugg::hir_with_macro_callsite(cx, cond_expr, "..").maybe_par(),
         if as_mut {
             ".as_mut()"
         } else if as_ref {
@@ -126,7 +127,7 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
 /// this construct is found, or None if this construct is not found.
 fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurence> {
     if_chain! {
-        if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly
+        if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly
         if !in_constant(cx, expr.hir_id);
         if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
             = higher::IfLet::hir(cx, expr);
@@ -146,11 +147,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
             let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
             let some_body = extract_body_from_expr(if_then)?;
             let none_body = extract_body_from_expr(if_else)?;
-            let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) {
-                "map_or"
-            } else {
-                "map_or_else"
-            };
+            let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" };
             let capture_name = id.name.to_ident_string();
             let (as_ref, as_mut) = match &let_expr.kind {
                 ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
@@ -183,8 +180,8 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
             Some(OptionIfLetElseOccurence {
                 option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
                 method_sugg: method_sugg.to_string(),
-                some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir(cx, some_body, "..")),
-                none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")),
+                some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")),
+                none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")),
             })
         } else {
             None
index 0f9e5ada3a8a4e1186a5f5471e9c270272ac440f..6dabbd4803117692a025804c9ccdd0302e077cd0 100644 (file)
@@ -19,6 +19,7 @@
     /// # let b = 2;
     /// a + b < a;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OVERFLOW_CHECK_CONDITIONAL,
     complexity,
     "overflow checks inspired by C which are likely to panic"
index 583c42b65631395a3acfec3a5748b917ff91e90f..8769c0452146435d1869b0ea30bfa74721052acb 100644 (file)
@@ -30,6 +30,7 @@
     ///     Err(String::from("error"))
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub PANIC_IN_RESULT_FN,
     restriction,
     "functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion"
index d8d9081d6f172ab72da22e9ad1697f5a55e9b521..edfac824ded987e8c47c5e45493c76b1beb83720 100644 (file)
@@ -17,6 +17,7 @@
     /// ```no_run
     /// panic!("even with a good reason");
     /// ```
+    #[clippy::version = "1.40.0"]
     pub PANIC,
     restriction,
     "usage of the `panic!` macro"
@@ -33,6 +34,7 @@
     /// ```no_run
     /// unimplemented!();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNIMPLEMENTED,
     restriction,
     "`unimplemented!` should not be present in production code"
@@ -49,6 +51,7 @@
     /// ```no_run
     /// todo!();
     /// ```
+    #[clippy::version = "1.40.0"]
     pub TODO,
     restriction,
     "`todo!` should not be present in production code"
@@ -65,6 +68,7 @@
     /// ```no_run
     /// unreachable!();
     /// ```
+    #[clippy::version = "1.40.0"]
     pub UNREACHABLE,
     restriction,
     "usage of the `unreachable!` macro"
index 4ec493e5f45e075f209523d0a7389d0de9eaa234..e827cdaae8728922580a9f4e7d9fbc7cafd5b3b6 100644 (file)
@@ -25,6 +25,7 @@
     ///    fn ne(&self, other: &Foo) -> bool { !(self == other) }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PARTIALEQ_NE_IMPL,
     complexity,
     "re-implementing `PartialEq::ne`"
index 6229b9608b3cbe5aad07d6316c374f70e30f2cff..3092ab8392a7231b99e23e74f71764ce88c21b1e 100644 (file)
@@ -65,6 +65,7 @@
     /// // Better
     /// fn foo(v: u32) {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRIVIALLY_COPY_PASS_BY_REF,
     pedantic,
     "functions taking small copyable arguments by reference"
@@ -98,6 +99,7 @@
     /// // Good
     /// fn foo(v: &TooLarge) {}
     /// ```
+    #[clippy::version = "1.49.0"]
     pub LARGE_TYPES_PASSED_BY_VALUE,
     pedantic,
     "functions taking large arguments by value"
index 3df7a72d2950984c2d27a2622e0a1c3e8561ac63..8ebee9bd04d3177c8aa8a4f2bfbdee035d41f998 100644 (file)
@@ -35,6 +35,7 @@
     /// x.push("bar");
     /// assert_eq!(x, PathBuf::from("/foo/bar"));
     /// ```
+    #[clippy::version = "1.36.0"]
     pub PATH_BUF_PUSH_OVERWRITE,
     nursery,
     "calling `push` with file system root on `PathBuf` can overwrite it"
index 018e6d611db77aa1e66744dc9dbb8d45bf3bbc87..c7d77d30927f854e2836665b13a65c43c5ebd53c 100644 (file)
@@ -73,6 +73,7 @@
     ///     *a += b;
     /// }
     /// ```
+    #[clippy::version = "1.47.0"]
     pub PATTERN_TYPE_MISMATCH,
     restriction,
     "type of pattern does not match the expression type"
index 1a8da00d9d616df75d96727cbe48496c1efbfd84..cc0533c9f5d1aa5b0abfb43e5050b42605f34265 100644 (file)
@@ -42,6 +42,7 @@
     /// ### Example
     /// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
     /// * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1
+    #[clippy::version = "pre 1.29.0"]
     pub PRECEDENCE,
     complexity,
     "operations where precedence may be unclear"
index 8a36e20fc973d678297d14718039e8913af8acc7..c08a19d520b607bac1658b9ce50ab64928d2f5e3 100644 (file)
@@ -70,6 +70,7 @@
     /// // Good
     /// fn foo(&[u32]) { .. }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PTR_ARG,
     style,
     "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
@@ -96,6 +97,7 @@
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CMP_NULL,
     style,
     "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
     /// ```ignore
     /// fn foo(&Foo) -> &mut Bar { .. }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUT_FROM_REF,
     correctness,
     "fns that create mutable refs from immutable ref args"
     /// // Good
     /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
     /// ```
+    #[clippy::version = "1.53.0"]
     pub INVALID_NULL_PTR_USAGE,
     correctness,
     "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
index 3258c9fb3fed0ef244813e52bf47f6d3a65f9722..3c126fc1ca69a27cc0862dcb3f44a8cf1f2df211 100644 (file)
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -30,6 +29,7 @@
     ///
     /// assert!(std::ptr::eq(a, b));
     /// ```
+    #[clippy::version = "1.49.0"]
     pub PTR_EQ,
     style,
     "use `std::ptr::eq` when comparing raw pointers"
@@ -41,7 +41,7 @@
 
 impl LateLintPass<'_> for PtrEq {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if in_macro(expr.span) {
+        if expr.span.from_expansion() {
             return;
         }
 
index cfb5287c6673fabe6a5a0642472916f768f9fe39..964564b57946b5784536ab86e62013fe073b869d 100644 (file)
@@ -38,6 +38,7 @@
     ///     ptr.add(offset);
     /// }
     /// ```
+    #[clippy::version = "1.30.0"]
     pub PTR_OFFSET_WITH_CAST,
     complexity,
     "unneeded pointer offset cast"
index f63ef163bcbd0f1b76ddfd92fcac4d38b50a48bc..a5531993ee6ad36bb038edf750c1f22b993fc6ba 100644 (file)
@@ -32,6 +32,7 @@
     /// ```ignore
     /// option?;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub QUESTION_MARK,
     style,
     "checks for expressions that could be replaced by the question mark operator"
index 4fa361fedafac434dbd26510c421506bed51e0b8..52c060bc42c7ab7dba51ee740aa1fcaaacdbc7cf 100644 (file)
@@ -35,6 +35,7 @@
     /// # let x = vec![1];
     /// x.iter().enumerate();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub RANGE_ZIP_WITH_LEN,
     complexity,
     "zipping iterator with a range when `enumerate()` would do"
@@ -72,6 +73,7 @@
     /// ```rust,ignore
     /// for x..=y { .. }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub RANGE_PLUS_ONE,
     pedantic,
     "`x..(y+1)` reads better as `x..=y`"
     /// ```rust,ignore
     /// for x..y { .. }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub RANGE_MINUS_ONE,
     pedantic,
     "`x..=(y-1)` reads better as `x..y`"
     ///     let sub = &arr[1..3];
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub REVERSED_EMPTY_RANGES,
     correctness,
     "reversing the limits of range expressions, resulting in empty ranges"
     ///# let x = 6;
     /// assert!((3..8).contains(&x));
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MANUAL_RANGE_CONTAINS,
     style,
     "manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
index 0eba6633ee19b36650bd3ee15b863fa43dba4661..1cf349f8aa7c7daa7526a25aee610df890de0904 100644 (file)
@@ -62,6 +62,7 @@ macro_rules! unwrap_or_continue {
     ///
     /// Path::new("/a/b").join("c").to_path_buf();
     /// ```
+    #[clippy::version = "1.32.0"]
     pub REDUNDANT_CLONE,
     perf,
     "`clone()` of an owned value that is going to be dropped immediately"
index 90e3c3f4b3e98f78121f2d23f43bc4a1c89148f6..0de282542fc3c73fc0e15446364b1505e1b3c3b8 100644 (file)
@@ -30,6 +30,7 @@
     /// // Good
     /// let a = 42
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub REDUNDANT_CLOSURE_CALL,
     complexity,
     "throwaway closures called in the expression they are defined"
index 68b256d29442acd199b3696f2094ed51129cfbd9..93dbe936d5841b2e9b9b289ea3e445c5a1857c4c 100644 (file)
@@ -36,6 +36,7 @@
     ///     print!("Moving on...");
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub REDUNDANT_ELSE,
     pedantic,
     "`else` branch that can be removed without changing semantics"
index 47df4917510ff97eadbb5e784cc8e754e1805396..0dea4a784b2170829f7f1a7be2c825f4dca7f2fd 100644 (file)
@@ -30,6 +30,7 @@
     /// ```ignore
     /// let foo = Foo { bar };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub REDUNDANT_FIELD_NAMES,
     style,
     "checks for fields in struct literals where shorthands could be used"
index 919d4e11e5a060637fb35ab2012a802f80f47aa6..2cee3c14d7f30f89db58dc7797d8090d4fe6d3a1 100644 (file)
@@ -26,6 +26,7 @@
     ///     pub fn internal_fn() { }
     /// }
     /// ```
+    #[clippy::version = "1.44.0"]
     pub REDUNDANT_PUB_CRATE,
     nursery,
     "Using `pub(crate)` visibility on items that are not crate visible due to the visibility of the module that contains them."
index 290348c4509ec0b028d11a6f39b377598c903711..b2bd0103d1114125f0849b91cb6a7abdb811e194 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::get_parent_expr;
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::is_type_lang_item;
-use clippy_utils::{get_parent_expr, in_macro};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
@@ -34,6 +34,7 @@
     ///     x
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub REDUNDANT_SLICING,
     complexity,
     "redundant slicing of the whole range of a type"
@@ -43,7 +44,7 @@
 
 impl LateLintPass<'_> for RedundantSlicing {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if in_macro(expr.span) {
+        if expr.span.from_expansion() {
             return;
         }
 
index d5a1a61da6bf4aac5cf769ee233a9faeeb3b4e19..ea5064217abe55ac47b63a6b91e9ef0369059547 100644 (file)
@@ -27,6 +27,7 @@
     ///  const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
     ///  static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
     /// ```
+    #[clippy::version = "1.37.0"]
     pub REDUNDANT_STATIC_LIFETIMES,
     style,
     "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them."
index d543832e314e97a197a38f6846d873513bf117f8..909d6971a5497305a7937bd7b70984e7ffb729b6 100644 (file)
@@ -28,6 +28,7 @@
     /// ```rust,ignore
     /// let x: Option<&u32> = Some(&0u32);
     /// ```
+    #[clippy::version = "1.49.0"]
     pub REF_OPTION_REF,
     pedantic,
     "use `Option<&T>` instead of `&Option<&T>`"
index 77b6e60d89398e61c55f2377bed0933d1125e20d..22ae7a291d00e5486e9ef8cb6a7b306aefda8257 100644 (file)
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use clippy_utils::source::{snippet_opt, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use if_chain::if_chain;
@@ -31,6 +30,7 @@
     /// let a = f(b);
     /// let c = d;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DEREF_ADDROF,
     complexity,
     "use of `*&` or `*&mut` in an expression"
@@ -50,7 +50,7 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
         if_chain! {
             if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind;
             if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind;
-            if !in_macro(addrof_target.span);
+            if !addrof_target.span.from_expansion();
             then {
                 let mut applicability = Applicability::MachineApplicable;
                 let sugg = if e.span.from_expansion() {
@@ -125,6 +125,7 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
     /// # let point = Point(30, 20);
     /// let x = point.0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub REF_IN_DEREF,
     complexity,
     "Use of reference in auto dereference expression."
index 5d08aee1e5f8880f63e1382bb74635e3d85c32d1..8e5983b4773a8cd418d2e26565f42ac2f14ac8a8 100644 (file)
@@ -22,6 +22,7 @@
     /// ```ignore
     /// Regex::new("|")
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INVALID_REGEX,
     correctness,
     "invalid regular expressions"
@@ -46,6 +47,7 @@
     /// ```ignore
     /// Regex::new("^foobar")
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRIVIAL_REGEX,
     nursery,
     "trivial regular expressions"
index cf94c0e97d930f166ec5a2b35da04c789dbc10e7..b5dd2de633742813a9b72347cf1fbc2e09724a44 100644 (file)
@@ -1,6 +1,5 @@
 use clippy_utils::consts::{constant_context, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
@@ -36,6 +35,7 @@
     ///     let x = String::from("hello world").clone();
     /// }
     /// ```
+    #[clippy::version = "1.47.0"]
     pub REPEAT_ONCE,
     complexity,
     "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
@@ -49,7 +49,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
             if let ExprKind::MethodCall(path, _, [receiver, count], _) = &expr.kind;
             if path.ident.name == sym!(repeat);
             if constant_context(cx, cx.typeck_results()).expr(count) == Some(Constant::Int(1));
-            if !in_macro(receiver.span);
+            if !receiver.span.from_expansion();
             then {
                 let ty = cx.typeck_results().expr_ty(receiver).peel_refs();
                 if ty.is_str() {
index ae85b7087e7b5add94b7cea8939f20f304f82377..494bc7dda18e76008aebf3460fb1d64b4a88d0ee 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{fn_def_id, in_macro, path_to_local_id};
+use clippy_utils::{fn_def_id, path_to_local_id};
 use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
 use rustc_errors::Applicability;
@@ -37,6 +37,7 @@
     ///     String::new()
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LET_AND_RETURN,
     style,
     "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
@@ -62,6 +63,7 @@
     ///     x
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_RETURN,
     style,
     "using a return statement like `return expr;` where an expression would suffice"
@@ -90,8 +92,7 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
             if !last_statement_borrows(cx, initexpr);
             if !in_external_macro(cx.sess(), initexpr.span);
             if !in_external_macro(cx.sess(), retexpr.span);
-            if !in_external_macro(cx.sess(), local.span);
-            if !in_macro(local.span);
+            if !local.span.from_expansion();
             then {
                 span_lint_and_then(
                     cx,
index 737ff634e449c4ff4406667e8f4d9e2afd4a6262..1bbaa104e60b17e8ab591e51c6c47fd27e45b258 100644 (file)
@@ -11,7 +11,7 @@
 
 declare_clippy_lint! {
     /// ### What it does
-    /// It lints if a struct has two method with same time:
+    /// It lints if a struct has two methods with the same name:
     /// one from a trait, another not from trait.
     ///
     /// ### Why is this bad?
@@ -33,6 +33,7 @@
     ///     fn foo(&self) {}
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub SAME_NAME_METHOD,
     restriction,
     "two method with same name"
@@ -99,7 +100,7 @@ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
                                         cx,
                                         SAME_NAME_METHOD,
                                         *impl_span,
-                                        "method's name is same to an existing method in a trait",
+                                        "method's name is the same as an existing method in a trait",
                                         |diag| {
                                             diag.span_note(
                                                 trait_method_span,
@@ -138,7 +139,7 @@ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
                                         cx,
                                         SAME_NAME_METHOD,
                                         impl_span,
-                                        "method's name is same to an existing method in a trait",
+                                        "method's name is the same as an existing method in a trait",
                                         |diag| {
                                             // TODO should we `span_note` on every trait?
                                             // iterate on trait_spans?
index fbd65fef7d11be2ede59c3e3002fdadd44cda52a..b14f0518bdb77807751ee8041097a98ef96e10ff 100644 (file)
@@ -30,6 +30,7 @@
     ///     a.y = a.y;
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub SELF_ASSIGNMENT,
     correctness,
     "explicit self-assignment"
index 9390378d789cdddec8c5b27f5156843e7366169d..d386663e49858eda7d123d04dd2bad7fb0de7a82 100644 (file)
@@ -32,6 +32,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.55.0"]
     pub SELF_NAMED_CONSTRUCTORS,
     style,
     "method should not have the same name as the type it is implemented for"
@@ -75,7 +76,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<
             let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
             if let Some(Node::Item(x)) = cx.tcx.hir().find(self_id);
             let type_name = x.ident.name.as_str().to_lowercase();
-            if impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace("_", "") == type_name;
+            if impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace('_', "") == type_name;
 
             then {
                 span_lint(
index c0e4914efe0bd1778a8ba68d4505c65048bdb310..0b3bbbc815580841e701e22d419e37e6ecc336e3 100644 (file)
@@ -29,6 +29,7 @@
     ///     println!("Hello world");
     /// }
     /// ```
+    #[clippy::version = "1.52.0"]
     pub SEMICOLON_IF_NOTHING_RETURNED,
     pedantic,
     "add a semicolon if nothing is returned"
@@ -44,7 +45,7 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
             let t_expr = cx.typeck_results().expr_ty(expr);
             if t_expr.is_unit();
             if let snippet = snippet_with_macro_callsite(cx, expr.span, "}");
-            if !snippet.ends_with('}');
+            if !snippet.ends_with('}') && !snippet.ends_with(';');
             if cx.sess().source_map().is_multiline(block.span);
             then {
                 // filter out the desugared `for` loop
index 2cd0f85999cf5bca682dd2103c246a544870f9d5..a38b3c4ab69b70fe10c49e8d73712720661a6226 100644 (file)
@@ -15,6 +15,7 @@
     /// ### Example
     /// Implementing `Visitor::visit_string` but not
     /// `Visitor::visit_str`.
+    #[clippy::version = "pre 1.29.0"]
     pub SERDE_API_MISUSE,
     correctness,
     "various things that will negatively affect your serde experience"
index 64841f33cc385afa389801c7ab9f19d2d3032514..f6880af0cab28a068709a113ab03b81479db97c3 100644 (file)
@@ -29,6 +29,7 @@
     /// // Good
     /// let y = &x; // use different variable name
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHADOW_SAME,
     restriction,
     "rebinding a name to itself, e.g., `let mut x = &mut x`"
@@ -55,6 +56,7 @@
     /// let x = 2;
     /// let y = x + 1;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHADOW_REUSE,
     restriction,
     "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`"
@@ -84,6 +86,7 @@
     /// // Good
     /// let w = z; // use different variable name
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHADOW_UNRELATED,
     restriction,
     "rebinding a name without even using the original value"
@@ -102,11 +105,16 @@ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
             PatKind::Binding(_, hir_id, ident, _) => (hir_id, ident),
             _ => return,
         };
+
+        if pat.span.desugaring_kind().is_some() {
+            return;
+        }
+
         if ident.span.from_expansion() || ident.span.is_dummy() {
             return;
         }
-        let HirId { owner, local_id } = id;
 
+        let HirId { owner, local_id } = id;
         // get (or insert) the list of items for this owner and symbol
         let data = self.bindings.last_mut().unwrap();
         let items_with_name = data.entry(ident.name).or_default();
index f6487b8c46bd49b2438dfd943cbe71c9bf5116c0..28d32203da9d73136f7b0c0a69a75af8690e8b21 100644 (file)
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
-use clippy_utils::in_macro;
 use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
@@ -28,6 +27,7 @@
     ///     regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
     /// }
     /// ```
+    #[clippy::version = "1.43.0"]
     pub SINGLE_COMPONENT_PATH_IMPORTS,
     style,
     "imports with single component path are redundant"
@@ -110,7 +110,7 @@ fn track_uses(
     single_use_usages: &mut Vec<(Symbol, Span, bool)>,
     macros: &mut Vec<Symbol>,
 ) {
-    if in_macro(item.span) || item.vis.kind.is_pub() {
+    if item.span.from_expansion() || item.vis.kind.is_pub() {
         return;
     }
 
index 3e4e4a8d0c08b03ecdb077d7eecf899a8f34c18b..df1e85afdd799a917fad95e4325de826050faa36 100644 (file)
@@ -29,6 +29,7 @@
     /// let mut y = [2u8; SIZE];
     /// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
     /// ```
+    #[clippy::version = "1.50.0"]
     pub SIZE_OF_IN_ELEMENT_COUNT,
     correctness,
     "using `size_of::<T>` or `size_of_val::<T>` where a count of elements of `T` is expected"
index 3608fe1472dda5b53eb07a454bd6246b9d47db58..1ae772ef70b124ae31244efd4ca23cc2058b7e38 100644 (file)
@@ -36,6 +36,7 @@
     /// let mut vec1 = vec![0; len];
     /// let mut vec2 = vec![0; len];
     /// ```
+    #[clippy::version = "1.32.0"]
     pub SLOW_VECTOR_INITIALIZATION,
     perf,
     "slow vector initialization"
index 4ea1293d504d3e30a3e6cf94ac7e6fe4e143df23..953d21e07a3908063d2ab59f13051ffa942f4f74 100644 (file)
@@ -28,6 +28,7 @@
     /// let mut vec = vec![2, 1, 3];
     /// vec.sort_unstable();
     /// ```
+    #[clippy::version = "1.47.0"]
     pub STABLE_SORT_PRIMITIVE,
     perf,
     "use of sort() when sort_unstable() is equivalent"
index 6435107b8b4643f02f94078b7fe0c70802458d56..368274440d5dcb23551ed30be7d4a8ba5f808ece 100644 (file)
@@ -31,6 +31,7 @@
     /// x += ", World";
     /// x.push_str(", World");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_ADD_ASSIGN,
     pedantic,
     "using `x = x + ..` where x is a `String` instead of `push_str()`"
@@ -58,6 +59,7 @@
     /// let x = "Hello".to_owned();
     /// x + ", World";
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_ADD,
     restriction,
     "using `x + ..` where x is a `String` instead of `push_str()`"
     /// // Good
     /// let bs = b"a byte string";
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_LIT_AS_BYTES,
     nursery,
     "calling `as_bytes` on a string literal instead of using a byte string literal"
     /// ```rust,should_panic
     /// &"Ölkanne"[1..];
     /// ```
+    #[clippy::version = "1.58.0"]
     pub STRING_SLICE,
     restriction,
     "slicing a string"
@@ -227,6 +231,7 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
     /// ```rust
     /// let _ = &"Hello World!"[6..11];
     /// ```
+    #[clippy::version = "1.50.0"]
     pub STRING_FROM_UTF8_AS_BYTES,
     complexity,
     "casting string slices to byte slices and back"
@@ -371,6 +376,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
     /// // example code which does not raise clippy warning
     /// let _ = "str".to_owned();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STR_TO_STRING,
     restriction,
     "using `to_string()` on a `&str`, which should be `to_owned()`"
@@ -420,6 +426,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
     /// let msg = String::from("Hello World");
     /// let _ = msg.clone();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_TO_STRING,
     restriction,
     "using `to_string()` on a `String`, which should be `clone()`"
index 516fa3d95b4282cad0e8f803ec1209b2f3a03c7c..fee01fb0bd186db7999e817c8f3751e0d2cf6d82 100644 (file)
@@ -1,14 +1,14 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
-use clippy_utils::paths;
-use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::ty::{is_type_diagnostic_item, is_type_ref_to_diagnostic_item};
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::visitors::is_expr_unsafe;
+use clippy_utils::{get_parent_node, match_libc_symbol};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir as hir;
+use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, UnsafeSource};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -31,6 +31,7 @@
     /// let cstring = CString::new("foo").expect("CString::new failed");
     /// let len = cstring.as_bytes().len();
     /// ```
+    #[clippy::version = "1.55.0"]
     pub STRLEN_ON_C_STRINGS,
     complexity,
     "using `libc::strlen` on a `CString` or `CStr` value, while `as_bytes().len()` or `to_bytes().len()` respectively can be used instead"
 declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]);
 
 impl LateLintPass<'tcx> for StrlenOnCStrings {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if in_macro(expr.span) {
-            return;
-        }
-
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
-            if let hir::ExprKind::Call(func, [recv]) = expr.kind;
-            if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func.kind;
-
-            if (&paths::LIBC_STRLEN).iter().map(|x| Symbol::intern(x)).eq(
-                path.segments.iter().map(|seg| seg.ident.name));
-            if let hir::ExprKind::MethodCall(path, _, args, _) = recv.kind;
-            if args.len() == 1;
-            if !args.iter().any(|e| e.span.from_expansion());
+            if !expr.span.from_expansion();
+            if let ExprKind::Call(func, [recv]) = expr.kind;
+            if let ExprKind::Path(path) = &func.kind;
+            if let Some(did) = cx.qpath_res(path, func.hir_id).opt_def_id();
+            if match_libc_symbol(cx, did, "strlen");
+            if let ExprKind::MethodCall(path, _, [self_arg], _) = recv.kind;
+            if !recv.span.from_expansion();
             if path.ident.name == sym::as_ptr;
             then {
-                let cstring = &args[0];
-                let ty = cx.typeck_results().expr_ty(cstring);
-                let val_name = snippet_with_macro_callsite(cx, cstring.span, "..");
-                let sugg = if is_type_diagnostic_item(cx, ty, sym::cstring_type){
-                    format!("{}.as_bytes().len()", val_name)
-                } else if is_type_ref_to_diagnostic_item(cx, ty, sym::CStr){
-                    format!("{}.to_bytes().len()", val_name)
+                let ctxt = expr.span.ctxt();
+                let span = match get_parent_node(cx.tcx, expr.hir_id) {
+                    Some(Node::Block(&Block {
+                        rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), span, ..
+                    }))
+                    if span.ctxt() == ctxt && !is_expr_unsafe(cx, self_arg) => {
+                        span
+                    }
+                    _ => expr.span,
+                };
+
+                let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
+                let mut app = Applicability::MachineApplicable;
+                let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
+                let method_name = if is_type_diagnostic_item(cx, ty, sym::cstring_type) {
+                    "as_bytes"
+                } else if is_type_diagnostic_item(cx, ty, sym::CStr) {
+                    "to_bytes"
                 } else {
                     return;
                 };
@@ -69,11 +76,11 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
                 span_lint_and_sugg(
                     cx,
                     STRLEN_ON_C_STRINGS,
-                    expr.span,
+                    span,
                     "using `libc::strlen` on a `CString` or `CStr` value",
-                    "try this (you might also need to get rid of `unsafe` block in some cases):",
-                    sugg,
-                    Applicability::Unspecified // Sometimes unnecessary `unsafe` block
+                    "try this",
+                    format!("{}.{}().len()", val_name, method_name),
+                    app,
                 );
             }
         }
index 201aa06782405c17921c5c08e2c2b47b9162ef78..faf43fd9fc1ad536ff934eb4cd1d4d5f116e2931 100644 (file)
@@ -59,6 +59,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub SUSPICIOUS_OPERATION_GROUPINGS,
     nursery,
     "groupings of binary operations that look suspiciously like typos"
index 682fad00a13ee30d3e29db8676998d40d91e8490..a3195de81d15c7415be7d5f95a3626ad1c0ba54a 100644 (file)
@@ -25,6 +25,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SUSPICIOUS_ARITHMETIC_IMPL,
     suspicious,
     "suspicious use of operators in impl of arithmetic trait"
@@ -46,6 +47,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SUSPICIOUS_OP_ASSIGN_IMPL,
     suspicious,
     "suspicious use of operators in impl of OpAssign trait"
index ef26de5b6b933dd810b9a0c845e2269efab2fb3b..4c10b12437d7b5e3465c5e520529b626f5fc8a3a 100644 (file)
@@ -2,7 +2,7 @@
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{can_mut_borrow_both, differing_macro_contexts, eq_expr_value};
+use clippy_utils::{can_mut_borrow_both, differing_macro_contexts, eq_expr_value, std_or_core};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
@@ -35,6 +35,7 @@
     /// let mut b = 2;
     /// std::mem::swap(&mut a, &mut b);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MANUAL_SWAP,
     complexity,
     "manual swap of two variables"
@@ -60,6 +61,7 @@
     /// # let mut b = 2;
     /// std::mem::swap(&mut a, &mut b);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ALMOST_SWAPPED,
     correctness,
     "`foo = bar; bar = foo` sequence"
@@ -113,6 +115,8 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa
 
     let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability);
     let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability);
+    let Some(sugg) = std_or_core(cx) else { return };
+
     span_lint_and_then(
         cx,
         MANUAL_SWAP,
@@ -122,11 +126,11 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa
             diag.span_suggestion(
                 span,
                 "try",
-                format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr()),
+                format!("{}::mem::swap({}, {})", sugg, first.mut_addr(), second.mut_addr()),
                 applicability,
             );
             if !is_xor_based {
-                diag.note("or maybe you should use `std::mem::replace`?");
+                diag.note(&format!("or maybe you should use `{}::mem::replace`?", sugg));
             }
         },
     );
@@ -187,26 +191,30 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
                 };
 
                 let span = first.span.to(second.span);
+                let Some(sugg) = std_or_core(cx) else { return };
 
                 span_lint_and_then(cx,
-                                   ALMOST_SWAPPED,
-                                   span,
-                                   &format!("this looks like you are trying to swap{}", what),
-                                   |diag| {
-                                       if !what.is_empty() {
-                                           diag.span_suggestion(
-                                               span,
-                                               "try",
-                                               format!(
-                                                   "std::mem::swap({}, {})",
-                                                   lhs,
-                                                   rhs,
-                                               ),
-                                               Applicability::MaybeIncorrect,
-                                           );
-                                           diag.note("or maybe you should use `std::mem::replace`?");
-                                       }
-                                   });
+                    ALMOST_SWAPPED,
+                    span,
+                    &format!("this looks like you are trying to swap{}", what),
+                    |diag| {
+                        if !what.is_empty() {
+                            diag.span_suggestion(
+                                span,
+                                "try",
+                                format!(
+                                    "{}::mem::swap({}, {})",
+                                    sugg,
+                                    lhs,
+                                    rhs,
+                                ),
+                                Applicability::MaybeIncorrect,
+                            );
+                            diag.note(
+                                &format!("or maybe you should use `{}::mem::replace`?", sugg)
+                            );
+                        }
+                    });
             }
         }
     }
index 4a67cabf323a6a1489f84d485bce350acc98a8cf..c9b4b245f4cc25019d130f05b33151bf80a81bdf 100644 (file)
@@ -51,6 +51,7 @@
     ///    second_string: String,
     ///}
     /// ```
+    #[clippy::version = "1.41.0"]
     pub TABS_IN_DOC_COMMENTS,
     style,
     "using tabs in doc comments is not recommended"
index a9da690339ccfaa997c50848f0d40d78a6a856bc..3766b8f8ed10d6aed395454ffcaa2f8e1ce12bd8 100644 (file)
@@ -17,6 +17,7 @@
     /// ```rust
     /// (0, 0).0 = 1
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TEMPORARY_ASSIGNMENT,
     complexity,
     "assignments to temporaries"
index 1c14a9199950cc5e3e1d070f6bf366ec63ecc35c..5eb58b47838298d493c0c4c88def60ec2e0182c0 100644 (file)
@@ -28,6 +28,7 @@
     /// # let radix = 10;
     /// let is_digit = c.is_digit(radix);
     /// ```
+    #[clippy::version = "1.41.0"]
     pub TO_DIGIT_IS_SOME,
     style,
     "`char.is_digit()` is clearer"
index b7414cec87cd2f3a6600d2525d61a3bea3625e88..f8b6bdcd3e15ed413e98e9530045fa5bc5e6fc2a 100644 (file)
@@ -39,6 +39,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub TO_STRING_IN_DISPLAY,
     correctness,
     "`to_string` method used while implementing `Display` trait"
index c216a1f81ea547630840e7403bd0f8ebe4820204..47c0a84cd4630ee97a94f3ebfc29f7987ea4e3ff 100644 (file)
@@ -28,6 +28,7 @@
     ///     last: [u32; 0],
     /// }
     /// ```
+    #[clippy::version = "1.58.0"]
     pub TRAILING_EMPTY_ARRAY,
     nursery,
     "struct with a trailing zero-sized array but without `#[repr(C)]` or another `repr` attribute"
index 79367c4230c2abbaaa842fd37a19bf24ff34c5dc..fb4abceac25e2ccab72372e32f4094c1d8742e81 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::{in_macro, SpanlessHash};
+use clippy_utils::SpanlessHash;
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::unhash::UnhashMap;
@@ -28,6 +28,7 @@
     /// ```rust
     /// pub fn foo<T>(t: T) where T: Copy + Clone {}
     /// ```
+    #[clippy::version = "1.38.0"]
     pub TYPE_REPETITION_IN_BOUNDS,
     pedantic,
     "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
@@ -57,6 +58,7 @@
     /// ```rust
     /// fn func<T>(arg: T) where T: Clone + Default {}
     /// ```
+    #[clippy::version = "1.47.0"]
     pub TRAIT_DUPLICATION_IN_BOUNDS,
     pedantic,
     "Check if the same trait bounds are specified twice during a function declaration"
@@ -93,7 +95,7 @@ fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)
 
 impl TraitBounds {
     fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
-        if in_macro(gen.span) {
+        if gen.span.from_expansion() {
             return;
         }
         let hash = |ty| -> u64 {
@@ -107,7 +109,7 @@ fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
             if_chain! {
                 if let WherePredicate::BoundPredicate(ref p) = bound;
                 if p.bounds.len() as u64 <= self.max_trait_bounds;
-                if !in_macro(p.span);
+                if !p.span.from_expansion();
                 let h = hash(p.bounded_ty);
                 if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>());
 
@@ -151,7 +153,7 @@ fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
 }
 
 fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
-    if in_macro(gen.span) || gen.params.is_empty() || gen.where_clause.predicates.is_empty() {
+    if gen.span.from_expansion() || gen.params.is_empty() || gen.where_clause.predicates.is_empty() {
         return;
     }
 
@@ -170,7 +172,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
     for predicate in gen.where_clause.predicates {
         if_chain! {
             if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
-            if !in_macro(bound_predicate.span);
+            if !bound_predicate.span.from_expansion();
             if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
             if let Some(segment) = segments.first();
             if let Some(trait_resolutions_direct) = map.get(&segment.ident);
index e6acf1a94c9299c6b2c59abfaa83a15da0441cb7..3ad4ec74bf51c1bdde228e00f451c3685fa92e31 100644 (file)
@@ -36,6 +36,7 @@
     /// ```ignore
     /// let ptr: *const T = core::intrinsics::transmute('x')
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRONG_TRANSMUTE,
     correctness,
     "transmutes that are confusing at best, undefined behaviour at worst and always useless"
@@ -55,6 +56,7 @@
     /// ```rust,ignore
     /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_TRANSMUTE,
     nursery,
     "transmutes that have the same to and from types or could be a cast/coercion"
@@ -80,6 +82,7 @@
     /// # let p: *const [i32] = &[];
     /// p as *const [u16];
     /// ```
+    #[clippy::version = "1.47.0"]
     pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
     complexity,
     "transmutes that could be a pointer cast"
     /// core::intrinsics::transmute(t) // where the result type is the same as
     ///                                // `*t` or `&t`'s
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CROSSPOINTER_TRANSMUTE,
     complexity,
     "transmutes that have to or from types that are a pointer to the other"
     /// // can be written:
     /// let _: &T = &*p;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_PTR_TO_REF,
     complexity,
     "transmutes from a pointer to a reference type"
     /// // should be:
     /// let _ = std::char::from_u32(x).unwrap();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_INT_TO_CHAR,
     complexity,
     "transmutes from an integer to a `char`"
     /// // should be:
     /// let _ = std::str::from_utf8(b).unwrap();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_BYTES_TO_STR,
     complexity,
     "transmutes from a `&[u8]` to a `&str`"
     /// // should be:
     /// let _: bool = x != 0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_INT_TO_BOOL,
     complexity,
     "transmutes from an integer to a `bool`"
     /// // should be:
     /// let _: f32 = f32::from_bits(1_u32);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_INT_TO_FLOAT,
     complexity,
     "transmutes from an integer to a float"
     /// // should be:
     /// let _: u32 = 1f32.to_bits();
     /// ```
+    #[clippy::version = "1.41.0"]
     pub TRANSMUTE_FLOAT_TO_INT,
     complexity,
     "transmutes from a float to an integer"
     /// // should be
     /// let x: [u8; 8] = 0i64.to_ne_bytes();
     /// ```
+    #[clippy::version = "1.58.0"]
     pub TRANSMUTE_NUM_TO_BYTES,
     complexity,
     "transmutes from a number to an array of `u8`"
     /// let _ = ptr as *const f32;
     /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_PTR_TO_PTR,
     pedantic,
     "transmutes from a pointer to a pointer / a reference to a reference"
     /// ```rust
     /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
     /// ```
+    #[clippy::version = "1.40.0"]
     pub UNSOUND_COLLECTION_TRANSMUTE,
     correctness,
     "transmute between collections of layout-incompatible types"
index ef80663d1da41ecdce20d5b885be1482f14b437f..7939dfedc3a2b4fb6d39555f29a3596828895861 100644 (file)
@@ -25,6 +25,7 @@
     /// ```rust
     /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
     /// ```
+    #[clippy::version = "1.35.0"]
     pub TRANSMUTING_NULL,
     correctness,
     "transmutes from a null pointer to a reference, which is undefined behavior"
index e9ec120a7f958d78eb8e96441c57277515cfe2c3..e0e7ec9a452c139b19d4aeabe0dc99d4981935bb 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{snippet, snippet_with_macro_callsite};
+use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, get_parent_expr, in_macro, is_lang_ctor, match_def_path, paths};
+use clippy_utils::{get_parent_expr, is_lang_ctor, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::ResultErr;
@@ -10,7 +10,7 @@
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
+use rustc_span::{hygiene, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -41,6 +41,7 @@
     ///     Ok(0)
     /// }
     /// ```
+    #[clippy::version = "1.38.0"]
     pub TRY_ERR,
     style,
     "return errors explicitly rather than hiding them behind a `?`"
@@ -93,15 +94,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 };
 
                 let expr_err_ty = cx.typeck_results().expr_ty(err_arg);
-                let differing_contexts = differing_macro_contexts(expr.span, err_arg.span);
-
-                let origin_snippet = if in_macro(expr.span) && in_macro(err_arg.span) && differing_contexts {
-                    snippet(cx, err_arg.span.ctxt().outer_expn_data().call_site, "_")
-                } else if err_arg.span.from_expansion() && !in_macro(expr.span) {
-                    snippet_with_macro_callsite(cx, err_arg.span, "_")
-                } else {
-                    snippet(cx, err_arg.span, "_")
-                };
+                let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt());
+                let mut applicability = Applicability::MachineApplicable;
+                let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability);
                 let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
                     "" // already returns
                 } else {
@@ -120,7 +115,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     "returning an `Err(_)` with the `?` operator",
                     "try this",
                     suggestion,
-                    Applicability::MachineApplicable
+                    applicability,
                 );
             }
         }
index bbe07db5358cdc805be57a209fc87c22871b24ee..5a7ef760a3025dcbf890a3175fe1d31200fed05c 100644 (file)
@@ -43,6 +43,7 @@
     ///     values: Vec<Foo>,
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub BOX_COLLECTION,
     perf,
     "usage of `Box<Vec<T>>`, vector elements are already on the heap"
@@ -75,6 +76,7 @@
     ///     values: Vec<i32>,
     /// }
     /// ```
+    #[clippy::version = "1.33.0"]
     pub VEC_BOX,
     complexity,
     "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap"
     ///     Contents::None
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OPTION_OPTION,
     pedantic,
     "usage of `Option<Option<T>>`"
     /// # use std::collections::LinkedList;
     /// let x: LinkedList<usize> = LinkedList::new();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LINKEDLIST,
     pedantic,
     "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`"
     /// ```rust,ignore
     /// fn foo(bar: &T) { ... }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BORROWED_BOX,
     complexity,
     "a borrow of a boxed type"
     /// ```rust
     /// fn foo(bar: &usize) {}
     /// ```
+    #[clippy::version = "1.44.0"]
     pub REDUNDANT_ALLOCATION,
     perf,
     "redundant allocation"
     /// ```rust,ignore
     /// fn foo(interned: Rc<str>) { ... }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub RC_BUFFER,
     restriction,
     "shared ownership of a buffer type"
     ///     inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TYPE_COMPLEXITY,
     complexity,
     "usage of very complex types that might be better factored into `type` definitions"
     /// use std::cell::RefCell
     /// fn foo(interned: Rc<RefCell<i32>>) { ... }
     /// ```
+    #[clippy::version = "1.55.0"]
     pub RC_MUTEX,
     restriction,
     "usage of `Rc<Mutex<T>>`"
index 11aef50991b0a1415a12dc83077de7246fe53d03..ccc49caf47c7a581dbc3ead8e898c21a24d3cdb8 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::is_lint_allowed;
 use clippy_utils::source::{indent_of, reindent_multiline, snippet};
-use clippy_utils::{in_macro, is_lint_allowed};
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, HirId, Local, UnsafeSource};
@@ -39,6 +39,7 @@
     /// // Safety: references are guaranteed to be non-null.
     /// let ptr = unsafe { NonNull::new_unchecked(a) };
     /// ```
+    #[clippy::version = "1.58.0"]
     pub UNDOCUMENTED_UNSAFE_BLOCKS,
     restriction,
     "creating an unsafe block without explaining why it is safe"
@@ -134,12 +135,12 @@ fn block_has_safety_comment(&mut self, tcx: TyCtxt<'_>, enclosing_hir_id: HirId,
 
         let enclosing_scope_span = map.opt_span(enclosing_hir_id)?;
 
-        let between_span = if in_macro(block_span) {
+        let between_span = if block_span.from_expansion() {
             self.macro_expansion = true;
-            enclosing_scope_span.with_hi(block_span.hi())
+            enclosing_scope_span.with_hi(block_span.hi()).source_callsite()
         } else {
             self.macro_expansion = false;
-            enclosing_scope_span.to(block_span)
+            enclosing_scope_span.to(block_span).source_callsite()
         };
 
         let file_name = source_map.span_to_filename(between_span);
@@ -149,6 +150,8 @@ fn block_has_safety_comment(&mut self, tcx: TyCtxt<'_>, enclosing_hir_id: HirId,
         let lex_end = (between_span.hi().0 - source_file.start_pos.0) as usize;
         let src_str = source_file.src.as_ref()?[lex_start..lex_end].to_string();
 
+        let source_start_pos = source_file.start_pos.0 as usize + lex_start;
+
         let mut pos = 0;
         let mut comment = false;
 
@@ -171,7 +174,7 @@ fn block_has_safety_comment(&mut self, tcx: TyCtxt<'_>, enclosing_hir_id: HirId,
                     if comment {
                         // Get the line number of the "comment" (really wherever the trailing whitespace ended)
                         let comment_line_num = source_file
-                            .lookup_file_pos_with_col_display(BytePos((lex_start + pos).try_into().unwrap()))
+                            .lookup_file_pos(BytePos((source_start_pos + pos).try_into().unwrap()))
                             .0;
                         // Find the block/local's line number
                         let block_line_num = tcx.sess.source_map().lookup_char_pos(block_span.lo()).line;
index 095706165936237e7921422b29c23daa080bd84d..c58fa67a023881c6a74255f5adb80b6bc9204761 100644 (file)
@@ -28,6 +28,7 @@
     ///     std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
     /// }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub UNDROPPED_MANUALLY_DROPS,
     correctness,
     "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
index f49ce696a04b77d4832cca91d62f78b12964f72d..afd7be89a4e289c94764d31c0eb27f12de4ff5de 100644 (file)
@@ -20,6 +20,7 @@
     /// ### Example
     /// You don't see it, but there may be a zero-width space or soft hyphen
     /// some­where in this text.
+    #[clippy::version = "1.49.0"]
     pub INVISIBLE_CHARACTERS,
     correctness,
     "using an invisible character in a string literal, which is confusing"
@@ -27,7 +28,7 @@
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for non-ASCII characters in string literals.
+    /// Checks for non-ASCII characters in string and char literals.
     ///
     /// ### Why is this bad?
     /// Yeah, we know, the 90's called and wanted their charset
@@ -44,6 +45,7 @@
     /// ```rust
     /// let x = String::from("\u{20ac}");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NON_ASCII_LITERAL,
     restriction,
     "using any literal non-ASCII chars in a string literal instead of using the `\\u` escape"
@@ -62,6 +64,7 @@
     /// ### Example
     /// You may not see it, but "à"" and "à"" aren't the same string. The
     /// former when escaped is actually `"a\u{300}"` while the latter is `"\u{e0}"`.
+    #[clippy::version = "pre 1.29.0"]
     pub UNICODE_NOT_NFC,
     pedantic,
     "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)"
@@ -72,7 +75,7 @@
 impl LateLintPass<'_> for Unicode {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
         if let ExprKind::Lit(ref lit) = expr.kind {
-            if let LitKind::Str(_, _) = lit.node {
+            if let LitKind::Str(_, _) | LitKind::Char(_) = lit.node {
                 check_str(cx, lit.span, expr.hir_id);
             }
         }
@@ -103,9 +106,9 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
             "invisible character detected",
             "consider replacing the string with",
             string
-                .replace("\u{200B}", "\\u{200B}")
-                .replace("\u{ad}", "\\u{AD}")
-                .replace("\u{2060}", "\\u{2060}"),
+                .replace('\u{200B}', "\\u{200B}")
+                .replace('\u{ad}', "\\u{AD}")
+                .replace('\u{2060}', "\\u{2060}"),
             Applicability::MachineApplicable,
         );
     }
index f3e8b6881058f93907762baedb2a472ab74fc1bc..46cc76b150e4a20e5f4b63dd85874c38ce57b987 100644 (file)
@@ -52,6 +52,7 @@
     ///    // perform initialization with `remaining`
     ///    vec.set_len(...);  // Safe to call `set_len()` on initialized part
     ///    ```
+    #[clippy::version = "1.58.0"]
     pub UNINIT_VEC,
     correctness,
     "Vec with uninitialized data"
index a3a3f2d41c7323354af7c50b4b7b46ffecd32879..26b4e0f58a870beabd074916fad606a8ceb862da 100644 (file)
@@ -39,6 +39,7 @@
     ///        WithValue(x) => x.hash(&mut state),
     /// }
     /// ```
+    #[clippy::version = "1.58.0"]
     pub UNIT_HASH,
     correctness,
     "hashing a unit value, which does nothing"
index db0f412f2a18c8ebc5302fe2c6419c957d0a9a09..9fb8f2368994a89a2db0dffd348884ea204c5c73 100644 (file)
@@ -30,6 +30,7 @@
     /// let mut twins = vec!((1, 1), (2, 2));
     /// twins.sort_by_key(|x| { x.1; });
     /// ```
+    #[clippy::version = "1.47.0"]
     pub UNIT_RETURN_EXPECTING_ORD,
     correctness,
     "fn arguments of type Fn(...) -> Ord returning the unit type ()."
index 66b1abbe50b9d389bc623fbf3d1324fab541f07b..d9f5b53b413a03dd5734a2edadfa202f7bf7ee98 100644 (file)
@@ -21,6 +21,7 @@
     ///     1;
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LET_UNIT_VALUE,
     pedantic,
     "creating a `let` binding to a value of unit type, which usually can't be used afterwards"
@@ -68,6 +69,7 @@
     /// assert_eq!({ foo(); }, { bar(); });
     /// ```
     /// will always succeed
+    #[clippy::version = "pre 1.29.0"]
     pub UNIT_CMP,
     correctness,
     "comparing unit values"
@@ -88,6 +90,7 @@
     ///     baz(a);
     /// })
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNIT_ARG,
     complexity,
     "passing unit to a function"
index 1eafdee03521c31ea7dac69e7ae7d5906e8b7bad..0bcafde658a43046760479f766987764f8c45902 100644 (file)
@@ -24,6 +24,7 @@
     ///     // ...
     /// }
     /// ```
+    #[clippy::version = "1.44.0"]
     pub FN_ADDRESS_COMPARISONS,
     correctness,
     "comparison with an address of a function item"
@@ -47,6 +48,7 @@
     ///     ...
     /// }
     /// ```
+    #[clippy::version = "1.44.0"]
     pub VTABLE_ADDRESS_COMPARISONS,
     correctness,
     "comparison with an address of a trait vtable"
index 4cfd2df551f1bcda7b80e832309a9b3a8f3b5453..839a4bdab09de36e5a32b4ad335fc66382233be1 100644 (file)
@@ -26,6 +26,7 @@
     /// ```rust
     /// use std::io;
     /// ```
+    #[clippy::version = "1.53.0"]
     pub UNNECESSARY_SELF_IMPORTS,
     restriction,
     "imports ending in `::{self}`, which can be omitted"
index 26b56e0f2f316c69b7e53e9f5f50020f25290c3b..d024577f485317b55bb54acbc35f571bff8bf6d7 100644 (file)
@@ -39,6 +39,7 @@
     /// # let mut vec: Vec<A> = Vec::new();
     /// vec.sort_by_key(|a| a.foo());
     /// ```
+    #[clippy::version = "1.46.0"]
     pub UNNECESSARY_SORT_BY,
     complexity,
     "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
index c940cf077d11c291ec7ac24e09725856fa30c861..1728533f18b858caeb6b9814f37ff2dcdbbc5e78 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
-use clippy_utils::{contains_return, in_macro, is_lang_ctor, return_ty, visitors::find_all_ret_expressions};
+use clippy_utils::{contains_return, is_lang_ctor, return_ty, visitors::find_all_ret_expressions};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
@@ -49,6 +49,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub UNNECESSARY_WRAPS,
     pedantic,
     "functions that only return `Ok` or `Some`"
@@ -116,7 +117,7 @@ fn check_fn(
         let mut suggs = Vec::new();
         let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| {
             if_chain! {
-                if !in_macro(ret_expr.span);
+                if !ret_expr.span.from_expansion();
                 // Check if a function call.
                 if let ExprKind::Call(func, [arg]) = ret_expr.kind;
                 // Check if OPTION_SOME or RESULT_OK, depending on return type.
index d6cf7190abb04959099378856509d1ad4cbe0ea0..0bd151fed91cc57c50c2b0c8ca3a93dd09dd4880 100644 (file)
@@ -39,6 +39,7 @@
     ///     if let Some(0 | 2) = Some(0) {}
     /// }
     /// ```
+    #[clippy::version = "1.46.0"]
     pub UNNESTED_OR_PATTERNS,
     pedantic,
     "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
index 3c694af2b9deea410fae420b37d0887ab5e7a8f9..44b1989dbc68a4a4eb30b01491b0bc018546235d 100644 (file)
@@ -21,6 +21,7 @@
     /// extern crate crossbeam;
     /// use crossbeam::{spawn_unsafe as spawn};
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNSAFE_REMOVED_FROM_NAME,
     style,
     "`unsafe` removed from API names on import"
index f4808682b69279dc1e4445d7c00770c030748866..1ccb78425c29af05f3415a393603ad08be412012 100644 (file)
@@ -29,6 +29,7 @@
     /// }
     /// let number_future = async { get_random_number_improved() };
     /// ```
+    #[clippy::version = "1.54.0"]
     pub UNUSED_ASYNC,
     pedantic,
     "finds async functions with no await statements"
index 031b182bd2fa0cdb68cad28c905db1ae5376dc6e..d4b5c9770a271200bb0efc997fe7b77a843bc8cc 100644 (file)
@@ -29,6 +29,7 @@
     ///     Ok(())
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNUSED_IO_AMOUNT,
     correctness,
     "unused written/read amount"
index c961f995667952343e846eeee07c36b68cb214a5..aa105580ee354833284b04ca99d6638183018a9b 100644 (file)
@@ -29,6 +29,7 @@
     ///     fn method() {}
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub UNUSED_SELF,
     pedantic,
     "methods that contain a `self` argument but don't use it"
index 1164ac4938fb6a28edce06017a0c0082869eaa83..48c17fa2a40b25c543143a12dcd3f38a9f2bb633 100644 (file)
@@ -28,6 +28,7 @@
     /// ```rust
     /// fn return_unit() {}
     /// ```
+    #[clippy::version = "1.31.0"]
     pub UNUSED_UNIT,
     style,
     "needless unit expression"
index ebaa9dcbbf85818cbcf8c36e399b0a44074df5cf..71771aae44b2172c8dc0175474c2f8279d5ef77e 100644 (file)
@@ -39,6 +39,7 @@
     ///     do_something_with(value)
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_UNWRAP,
     complexity,
     "checks for calls of `unwrap[_err]()` that cannot fail"
@@ -65,6 +66,7 @@
     /// ```
     ///
     /// This code will always panic. The if condition should probably be inverted.
+    #[clippy::version = "pre 1.29.0"]
     pub PANICKING_UNWRAP,
     correctness,
     "checks for calls of `unwrap[_err]()` that will always fail"
@@ -229,8 +231,8 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
         } else {
             // find `unwrap[_err]()` calls:
             if_chain! {
-                if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind;
-                if let Some(id) = path_to_local(&args[0]);
+                if let ExprKind::MethodCall(method_name, _, [self_arg, ..], _) = expr.kind;
+                if let Some(id) = path_to_local(self_arg);
                 if [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name);
                 let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name);
                 if let Some(unwrappable) = self.unwrappables.iter()
index 6447e3fa2ca08a85d04716b6bd1f955ba8f5b931..994df85cb8ac4f7c5130275884a75f12e34491f6 100644 (file)
@@ -51,6 +51,7 @@
     ///     Ok(())
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub UNWRAP_IN_RESULT,
     restriction,
     "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`"
index dbf335a70c83118eee6fcc88f1829ab6e6baffd8..4773e35076057e0bf32a65a507d5e9c71faf4d79 100644 (file)
@@ -33,6 +33,7 @@
     /// ```rust
     /// struct HttpResponse;
     /// ```
+    #[clippy::version = "1.51.0"]
     pub UPPER_CASE_ACRONYMS,
     style,
     "capitalized acronyms are against the naming convention"
index 9ae50e47ca4c59ce899e0472b635f7d0e493a925..059f7f647f88f0e0f486a400a4608b6fbf422884 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::ty::same_type_and_consts;
-use clippy_utils::{in_macro, meets_msrv, msrvs};
+use clippy_utils::{meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -51,6 +51,7 @@
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USE_SELF,
     nursery,
     "unnecessary structure name repetition whereas `Self` is applicable"
@@ -197,7 +198,7 @@ fn check_body_post(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) {
 
     fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
         if_chain! {
-            if !in_macro(hir_ty.span);
+            if !hir_ty.span.from_expansion();
             if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
             if let Some(&StackItem::Check {
                 impl_id,
@@ -214,8 +215,8 @@ fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
             };
             if same_type_and_consts(ty, cx.tcx.type_of(impl_id));
             let hir = cx.tcx.hir();
-            let id = hir.get_parent_node(hir_ty.hir_id);
-            if !hir.opt_span(id).map_or(false, in_macro);
+            // prevents false positive on `#[derive(serde::Deserialize)]`
+            if !hir.span(hir.get_parent_node(hir_ty.hir_id)).in_derive_expansion();
             then {
                 span_lint(cx, hir_ty.span);
             }
@@ -224,7 +225,7 @@ fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
 
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
         if_chain! {
-            if !in_macro(expr.span);
+            if !expr.span.from_expansion();
             if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
             if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
             if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
index 88f11542072b849e7920c3893fb0bf0ce713ac49..0e4b32541c97785672a65a8e08eb86eda3ee7135 100644 (file)
@@ -28,6 +28,7 @@
     /// // Good
     /// let s: String = format!("hello");
     /// ```
+    #[clippy::version = "1.45.0"]
     pub USELESS_CONVERSION,
     complexity,
     "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` which perform useless conversions to the same type"
index f93d7782e2511bd0ad666ca1e2e6a08440c1dac7..d20bf3413185c1aa38cf6bed8a77af25a58d4759 100644 (file)
@@ -1,16 +1,16 @@
 //! A group of attributes that can be attached to Rust code in order
 //! to generate a clippy lint detecting said code automatically.
 
-use clippy_utils::get_attr;
+use clippy_utils::{get_attr, higher};
 use rustc_ast::ast::{LitFloatType, LitKind};
-use rustc_ast::walk_list;
+use rustc_ast::LitIntType;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
-use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
-use rustc_hir::{Block, Expr, ExprKind, Pat, PatKind, QPath, Stmt, StmtKind, TyKind};
+use rustc_hir::{ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::{Ident, Symbol};
+use std::fmt::{Display, Formatter, Write as _};
 
 declare_clippy_lint! {
     /// ### What it does
 
 declare_lint_pass!(Author => [LINT_AUTHOR]);
 
+/// Writes a line of output with indentation added
+macro_rules! out {
+    ($($t:tt)*) => {
+        println!("    {}", format_args!($($t)*))
+    };
+}
+
+/// The variables passed in are replaced with `&Binding`s where the `value` field is set
+/// to the original value of the variable. The `name` field is set to the name of the variable
+/// (using `stringify!`) and is adjusted to avoid duplicate names.
+/// Note that the `Binding` may be printed directly to output the `name`.
+macro_rules! bind {
+    ($self:ident $(, $name:ident)+) => {
+        $(let $name = & $self.bind(stringify!($name), $name);)+
+    };
+}
+
+/// Transforms the given `Option<T>` varibles into `OptionPat<Binding<T>>`.
+/// This displays as `Some($name)` or `None` when printed. The name of the inner binding
+/// is set to the name of the variable passed to the macro.
+macro_rules! opt_bind {
+    ($self:ident $(, $name:ident)+) => {
+        $(let $name = OptionPat::new($name.map(|o| $self.bind(stringify!($name), o)));)+
+    };
+}
+
+/// Creates a `Binding` that accesses the field of an existing `Binding`
+macro_rules! field {
+    ($binding:ident.$field:ident) => {
+        &Binding {
+            name: $binding.name.to_string() + stringify!(.$field),
+            value: $binding.value.$field,
+        }
+    };
+}
+
 fn prelude() {
     println!("if_chain! {{");
 }
@@ -66,674 +102,594 @@ fn done() {
 
 impl<'tcx> LateLintPass<'tcx> for Author {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-        if !has_attr(cx, item.hir_id()) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("item").visit_item(item);
-        done();
+        check_item(cx, item.hir_id());
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
-        if !has_attr(cx, item.hir_id()) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("item").visit_impl_item(item);
-        done();
+        check_item(cx, item.hir_id());
     }
 
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
-        if !has_attr(cx, item.hir_id()) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("item").visit_trait_item(item);
-        done();
+        check_item(cx, item.hir_id());
     }
 
-    fn check_variant(&mut self, cx: &LateContext<'tcx>, var: &'tcx hir::Variant<'_>) {
-        if !has_attr(cx, var.id) {
-            return;
-        }
-        prelude();
-        let parent_hir_id = cx.tcx.hir().get_parent_node(var.id);
-        PrintVisitor::new("var").visit_variant(var, &hir::Generics::empty(), parent_hir_id);
-        done();
-    }
-
-    fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'_>) {
-        if !has_attr(cx, field.hir_id) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("field").visit_field_def(field);
-        done();
+    fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
+        check_node(cx, arm.hir_id, |v| {
+            v.arm(&v.bind("arm", arm));
+        });
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if !has_attr(cx, expr.hir_id) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("expr").visit_expr(expr);
-        done();
-    }
-
-    fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
-        if !has_attr(cx, arm.hir_id) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("arm").visit_arm(arm);
-        done();
+        check_node(cx, expr.hir_id, |v| {
+            v.expr(&v.bind("expr", expr));
+        });
     }
 
     fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
-        if !has_attr(cx, stmt.hir_id) {
-            return;
-        }
         match stmt.kind {
             StmtKind::Expr(e) | StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return,
             _ => {},
         }
+        check_node(cx, stmt.hir_id, |v| {
+            v.stmt(&v.bind("stmt", stmt));
+        });
+    }
+}
+
+fn check_item(cx: &LateContext<'_>, hir_id: HirId) {
+    let hir = cx.tcx.hir();
+    if let Some(body_id) = hir.maybe_body_owned_by(hir_id) {
+        check_node(cx, hir_id, |v| {
+            v.expr(&v.bind("expr", &hir.body(body_id).value));
+        });
+    }
+}
+
+fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) {
+    if has_attr(cx, hir_id) {
         prelude();
-        PrintVisitor::new("stmt").visit_stmt(stmt);
+        f(&PrintVisitor::new(cx));
         done();
     }
+}
 
-    fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ForeignItem<'_>) {
-        if !has_attr(cx, item.hir_id()) {
-            return;
+struct Binding<T> {
+    name: String,
+    value: T,
+}
+
+impl<T> Display for Binding<T> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        f.write_str(&self.name)
+    }
+}
+
+struct OptionPat<T> {
+    pub opt: Option<T>,
+}
+
+impl<T> OptionPat<T> {
+    fn new(opt: Option<T>) -> Self {
+        Self { opt }
+    }
+
+    fn if_some(&self, f: impl Fn(&T)) {
+        if let Some(t) = &self.opt {
+            f(t);
+        }
+    }
+}
+
+impl<T: Display> Display for OptionPat<T> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        match &self.opt {
+            None => f.write_str("None"),
+            Some(node) => write!(f, "Some({node})"),
         }
-        prelude();
-        PrintVisitor::new("item").visit_foreign_item(item);
-        done();
     }
 }
 
-impl PrintVisitor {
-    #[must_use]
-    fn new(s: &'static str) -> Self {
+struct PrintVisitor<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    /// Fields are the current index that needs to be appended to pattern
+    /// binding names
+    ids: std::cell::Cell<FxHashMap<&'static str, u32>>,
+}
+
+#[allow(clippy::unused_self)]
+impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
+    fn new(cx: &'a LateContext<'tcx>) -> Self {
         Self {
-            ids: FxHashMap::default(),
-            current: s.to_owned(),
+            cx,
+            ids: std::cell::Cell::default(),
         }
     }
 
-    fn next(&mut self, s: &'static str) -> String {
-        use std::collections::hash_map::Entry::{Occupied, Vacant};
-        match self.ids.entry(s) {
-            // already there: start numbering from `1`
-            Occupied(mut occ) => {
-                let val = occ.get_mut();
-                *val += 1;
-                format!("{}{}", s, *val)
-            },
-            // not there: insert and return name as given
-            Vacant(vac) => {
-                vac.insert(0);
-                s.to_owned()
+    fn next(&self, s: &'static str) -> String {
+        let mut ids = self.ids.take();
+        let out = match *ids.entry(s).and_modify(|n| *n += 1).or_default() {
+            // first usage of the name, use it as is
+            0 => s.to_string(),
+            // append a number starting with 1
+            n => format!("{s}{n}"),
+        };
+        self.ids.set(ids);
+        out
+    }
+
+    fn bind<T>(&self, name: &'static str, value: T) -> Binding<T> {
+        let name = self.next(name);
+        Binding { name, value }
+    }
+
+    fn option<T: Copy>(&self, option: &Binding<Option<T>>, name: &'static str, f: impl Fn(&Binding<T>)) {
+        match option.value {
+            None => out!("if {option}.is_none();"),
+            Some(value) => {
+                let value = &self.bind(name, value);
+                out!("if let Some({value}) = {option};");
+                f(value);
             },
         }
     }
 
-    fn print_qpath(&mut self, path: &QPath<'_>) {
-        if let QPath::LangItem(lang_item, _) = *path {
-            println!(
-                "    if matches!({}, QPath::LangItem(LangItem::{:?}, _));",
-                self.current, lang_item,
-            );
+    fn slice<T>(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) {
+        if slice.value.is_empty() {
+            out!("if {slice}.is_empty();");
         } else {
-            print!("    if match_qpath({}, &[", self.current);
-            print_path(path, &mut true);
-            println!("]);");
+            out!("if {slice}.len() == {};", slice.value.len());
+            for (i, value) in slice.value.iter().enumerate() {
+                let name = format!("{slice}[{i}]");
+                f(&Binding { name, value });
+            }
         }
     }
-}
 
-struct PrintVisitor {
-    /// Fields are the current index that needs to be appended to pattern
-    /// binding names
-    ids: FxHashMap<&'static str, usize>,
-    /// the name that needs to be destructured
-    current: String,
-}
+    fn destination(&self, destination: &Binding<hir::Destination>) {
+        self.option(field!(destination.label), "label", |label| {
+            self.ident(field!(label.ident));
+        });
+    }
+
+    fn ident(&self, ident: &Binding<Ident>) {
+        out!("if {ident}.as_str() == {:?};", ident.value.as_str());
+    }
+
+    fn symbol(&self, symbol: &Binding<Symbol>) {
+        out!("if {symbol}.as_str() == {:?};", symbol.value.as_str());
+    }
 
-impl<'tcx> Visitor<'tcx> for PrintVisitor {
-    type Map = Map<'tcx>;
+    fn qpath(&self, qpath: &Binding<&QPath<'_>>) {
+        if let QPath::LangItem(lang_item, _) = *qpath.value {
+            out!("if matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _));");
+        } else {
+            out!("if match_qpath({qpath}, &[{}]);", path_to_string(qpath.value));
+        }
+    }
+
+    fn lit(&self, lit: &Binding<&Lit>) {
+        let kind = |kind| out!("if let LitKind::{kind} = {lit}.node;");
+        macro_rules! kind {
+            ($($t:tt)*) => (kind(format_args!($($t)*)));
+        }
+
+        match lit.value.node {
+            LitKind::Bool(val) => kind!("Bool({val:?})"),
+            LitKind::Char(c) => kind!("Char({c:?})"),
+            LitKind::Err(val) => kind!("Err({val})"),
+            LitKind::Byte(b) => kind!("Byte({b})"),
+            LitKind::Int(i, suffix) => {
+                let int_ty = match suffix {
+                    LitIntType::Signed(int_ty) => format!("LitIntType::Signed(IntTy::{int_ty:?})"),
+                    LitIntType::Unsigned(uint_ty) => format!("LitIntType::Unsigned(UintTy::{uint_ty:?})"),
+                    LitIntType::Unsuffixed => String::from("LitIntType::Unsuffixed"),
+                };
+                kind!("Int({i}, {int_ty})");
+            },
+            LitKind::Float(_, suffix) => {
+                let float_ty = match suffix {
+                    LitFloatType::Suffixed(suffix_ty) => format!("LitFloatType::Suffixed(FloatTy::{suffix_ty:?})"),
+                    LitFloatType::Unsuffixed => String::from("LitFloatType::Unsuffixed"),
+                };
+                kind!("Float(_, {float_ty})");
+            },
+            LitKind::ByteStr(ref vec) => {
+                bind!(self, vec);
+                kind!("ByteStr(ref {vec})");
+                out!("if let [{:?}] = **{vec};", vec.value);
+            },
+            LitKind::Str(s, _) => {
+                bind!(self, s);
+                kind!("Str({s}, _)");
+                self.symbol(s);
+            },
+        }
+    }
+
+    fn arm(&self, arm: &Binding<&hir::Arm<'_>>) {
+        self.pat(field!(arm.pat));
+        match arm.value.guard {
+            None => out!("if {arm}.guard.is_none();"),
+            Some(hir::Guard::If(expr)) => {
+                bind!(self, expr);
+                out!("if let Some(Guard::If({expr})) = {arm}.guard;");
+                self.expr(expr);
+            },
+            Some(hir::Guard::IfLet(pat, expr)) => {
+                bind!(self, pat, expr);
+                out!("if let Some(Guard::IfLet({pat}, {expr}) = {arm}.guard;");
+                self.pat(pat);
+                self.expr(expr);
+            },
+        }
+        self.expr(field!(arm.body));
+    }
 
     #[allow(clippy::too_many_lines)]
-    fn visit_expr(&mut self, expr: &Expr<'_>) {
-        print!("    if let ExprKind::");
-        let current = format!("{}.kind", self.current);
-        match expr.kind {
+    fn expr(&self, expr: &Binding<&hir::Expr<'_>>) {
+        if let Some(higher::While { condition, body }) = higher::While::hir(expr.value) {
+            bind!(self, condition, body);
+            out!(
+                "if let Some(higher::While {{ condition: {condition}, body: {body} }}) \
+                = higher::While::hir({expr});"
+            );
+            self.expr(condition);
+            self.expr(body);
+            return;
+        }
+
+        if let Some(higher::WhileLet {
+            let_pat,
+            let_expr,
+            if_then,
+        }) = higher::WhileLet::hir(expr.value)
+        {
+            bind!(self, let_pat, let_expr, if_then);
+            out!(
+                "if let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \
+                = higher::WhileLet::hir({expr});"
+            );
+            self.pat(let_pat);
+            self.expr(let_expr);
+            self.expr(if_then);
+            return;
+        }
+
+        if let Some(higher::ForLoop { pat, arg, body, .. }) = higher::ForLoop::hir(expr.value) {
+            bind!(self, pat, arg, body);
+            out!(
+                "if let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \
+                = higher::ForLoop::hir({expr});"
+            );
+            self.pat(pat);
+            self.expr(arg);
+            self.expr(body);
+            return;
+        }
+
+        let kind = |kind| out!("if let ExprKind::{kind} = {expr}.kind;");
+        macro_rules! kind {
+            ($($t:tt)*) => (kind(format_args!($($t)*)));
+        }
+
+        match expr.value.kind {
             ExprKind::Let(pat, expr, _) => {
-                let let_pat = self.next("pat");
-                let let_expr = self.next("expr");
-                println!("    Let(ref {}, ref {}, _) = {};", let_pat, let_expr, current);
-                self.current = let_expr;
-                self.visit_expr(expr);
-                self.current = let_pat;
-                self.visit_pat(pat);
+                bind!(self, pat, expr);
+                kind!("Let({pat}, {expr}, _)");
+                self.pat(pat);
+                self.expr(expr);
             },
             ExprKind::Box(inner) => {
-                let inner_pat = self.next("inner");
-                println!("Box(ref {}) = {};", inner_pat, current);
-                self.current = inner_pat;
-                self.visit_expr(inner);
+                bind!(self, inner);
+                kind!("Box({inner})");
+                self.expr(inner);
             },
             ExprKind::Array(elements) => {
-                let elements_pat = self.next("elements");
-                println!("Array(ref {}) = {};", elements_pat, current);
-                println!("    if {}.len() == {};", elements_pat, elements.len());
-                for (i, element) in elements.iter().enumerate() {
-                    self.current = format!("{}[{}]", elements_pat, i);
-                    self.visit_expr(element);
-                }
+                bind!(self, elements);
+                kind!("Array({elements})");
+                self.slice(elements, |e| self.expr(e));
             },
             ExprKind::Call(func, args) => {
-                let func_pat = self.next("func");
-                let args_pat = self.next("args");
-                println!("Call(ref {}, ref {}) = {};", func_pat, args_pat, current);
-                self.current = func_pat;
-                self.visit_expr(func);
-                println!("    if {}.len() == {};", args_pat, args.len());
-                for (i, arg) in args.iter().enumerate() {
-                    self.current = format!("{}[{}]", args_pat, i);
-                    self.visit_expr(arg);
-                }
+                bind!(self, func, args);
+                kind!("Call({func}, {args})");
+                self.expr(func);
+                self.slice(args, |e| self.expr(e));
             },
-            ExprKind::MethodCall(_method_name, ref _generics, _args, ref _fn_span) => {
-                println!(
-                    "MethodCall(ref method_name, ref generics, ref args, ref fn_span) = {};",
-                    current
-                );
-                println!("    // unimplemented: `ExprKind::MethodCall` is not further destructured at the moment");
+            ExprKind::MethodCall(method_name, _, args, _) => {
+                bind!(self, method_name, args);
+                kind!("MethodCall({method_name}, _, {args}, _)");
+                self.ident(field!(method_name.ident));
+                self.slice(args, |e| self.expr(e));
             },
             ExprKind::Tup(elements) => {
-                let elements_pat = self.next("elements");
-                println!("Tup(ref {}) = {};", elements_pat, current);
-                println!("    if {}.len() == {};", elements_pat, elements.len());
-                for (i, element) in elements.iter().enumerate() {
-                    self.current = format!("{}[{}]", elements_pat, i);
-                    self.visit_expr(element);
-                }
-            },
-            ExprKind::Binary(ref op, left, right) => {
-                let op_pat = self.next("op");
-                let left_pat = self.next("left");
-                let right_pat = self.next("right");
-                println!(
-                    "Binary(ref {}, ref {}, ref {}) = {};",
-                    op_pat, left_pat, right_pat, current
-                );
-                println!("    if BinOpKind::{:?} == {}.node;", op.node, op_pat);
-                self.current = left_pat;
-                self.visit_expr(left);
-                self.current = right_pat;
-                self.visit_expr(right);
-            },
-            ExprKind::Unary(ref op, inner) => {
-                let inner_pat = self.next("inner");
-                println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current);
-                self.current = inner_pat;
-                self.visit_expr(inner);
+                bind!(self, elements);
+                kind!("Tup({elements})");
+                self.slice(elements, |e| self.expr(e));
+            },
+            ExprKind::Binary(op, left, right) => {
+                bind!(self, op, left, right);
+                kind!("Binary({op}, {left}, {right})");
+                out!("if BinOpKind::{:?} == {op}.node;", op.value.node);
+                self.expr(left);
+                self.expr(right);
+            },
+            ExprKind::Unary(op, inner) => {
+                bind!(self, inner);
+                kind!("Unary(UnOp::{op:?}, {inner})");
+                self.expr(inner);
             },
             ExprKind::Lit(ref lit) => {
-                let lit_pat = self.next("lit");
-                println!("Lit(ref {}) = {};", lit_pat, current);
-                match lit.node {
-                    LitKind::Bool(val) => println!("    if let LitKind::Bool({:?}) = {}.node;", val, lit_pat),
-                    LitKind::Char(c) => println!("    if let LitKind::Char({:?}) = {}.node;", c, lit_pat),
-                    LitKind::Err(val) => println!("    if let LitKind::Err({}) = {}.node;", val, lit_pat),
-                    LitKind::Byte(b) => println!("    if let LitKind::Byte({}) = {}.node;", b, lit_pat),
-                    // FIXME: also check int type
-                    LitKind::Int(i, _) => println!("    if let LitKind::Int({}, _) = {}.node;", i, lit_pat),
-                    LitKind::Float(_, LitFloatType::Suffixed(_)) => println!(
-                        "    if let LitKind::Float(_, LitFloatType::Suffixed(_)) = {}.node;",
-                        lit_pat
-                    ),
-                    LitKind::Float(_, LitFloatType::Unsuffixed) => println!(
-                        "    if let LitKind::Float(_, LitFloatType::Unsuffixed) = {}.node;",
-                        lit_pat
-                    ),
-                    LitKind::ByteStr(ref vec) => {
-                        let vec_pat = self.next("vec");
-                        println!("    if let LitKind::ByteStr(ref {}) = {}.node;", vec_pat, lit_pat);
-                        println!("    if let [{:?}] = **{};", vec, vec_pat);
-                    },
-                    LitKind::Str(ref text, _) => {
-                        let str_pat = self.next("s");
-                        println!("    if let LitKind::Str(ref {}, _) = {}.node;", str_pat, lit_pat);
-                        println!("    if {}.as_str() == {:?}", str_pat, &*text.as_str());
-                    },
+                bind!(self, lit);
+                kind!("Lit(ref {lit})");
+                self.lit(lit);
+            },
+            ExprKind::Cast(expr, cast_ty) => {
+                bind!(self, expr, cast_ty);
+                kind!("Cast({expr}, {cast_ty})");
+                if let TyKind::Path(ref qpath) = cast_ty.value.kind {
+                    bind!(self, qpath);
+                    out!("if let TyKind::Path(ref {qpath}) = {cast_ty}.kind;");
+                    self.qpath(qpath);
                 }
-            },
-            ExprKind::Cast(expr, ty) => {
-                let cast_pat = self.next("expr");
-                let cast_ty = self.next("cast_ty");
-                let qp_label = self.next("qp");
-
-                println!("Cast(ref {}, ref {}) = {};", cast_pat, cast_ty, current);
-                if let TyKind::Path(ref qp) = ty.kind {
-                    println!("    if let TyKind::Path(ref {}) = {}.kind;", qp_label, cast_ty);
-                    self.current = qp_label;
-                    self.print_qpath(qp);
-                }
-                self.current = cast_pat;
-                self.visit_expr(expr);
+                self.expr(expr);
             },
             ExprKind::Type(expr, _ty) => {
-                let cast_pat = self.next("expr");
-                println!("Type(ref {}, _) = {};", cast_pat, current);
-                self.current = cast_pat;
-                self.visit_expr(expr);
-            },
-            ExprKind::Loop(body, _, des, _) => {
-                let body_pat = self.next("body");
-                let label_pat = self.next("label");
-                println!(
-                    "Loop(ref {}, ref {}, LoopSource::{:?}) = {};",
-                    body_pat, label_pat, des, current
-                );
-                self.current = body_pat;
-                self.visit_block(body);
-            },
-            ExprKind::If(cond, then, ref opt_else) => {
-                let cond_pat = self.next("cond");
-                let then_pat = self.next("then");
-                if let Some(else_) = *opt_else {
-                    let else_pat = self.next("else_");
-                    println!(
-                        "If(ref {}, ref {}, Some(ref {})) = {};",
-                        cond_pat, then_pat, else_pat, current
-                    );
-                    self.current = else_pat;
-                    self.visit_expr(else_);
-                } else {
-                    println!("If(ref {}, ref {}, None) = {};", cond_pat, then_pat, current);
-                }
-                self.current = cond_pat;
-                self.visit_expr(cond);
-                self.current = then_pat;
-                self.visit_expr(then);
-            },
-            ExprKind::Match(expr, arms, des) => {
-                let expr_pat = self.next("expr");
-                let arms_pat = self.next("arms");
-                println!(
-                    "Match(ref {}, ref {}, MatchSource::{:?}) = {};",
-                    expr_pat, arms_pat, des, current
-                );
-                self.current = expr_pat;
-                self.visit_expr(expr);
-                println!("    if {}.len() == {};", arms_pat, arms.len());
-                for (i, arm) in arms.iter().enumerate() {
-                    self.current = format!("{}[{}].body", arms_pat, i);
-                    self.visit_expr(arm.body);
-                    if let Some(ref guard) = arm.guard {
-                        let guard_pat = self.next("guard");
-                        println!("    if let Some(ref {}) = {}[{}].guard;", guard_pat, arms_pat, i);
-                        match guard {
-                            hir::Guard::If(if_expr) => {
-                                let if_expr_pat = self.next("expr");
-                                println!("    if let Guard::If(ref {}) = {};", if_expr_pat, guard_pat);
-                                self.current = if_expr_pat;
-                                self.visit_expr(if_expr);
-                            },
-                            hir::Guard::IfLet(if_let_pat, if_let_expr) => {
-                                let if_let_pat_pat = self.next("pat");
-                                let if_let_expr_pat = self.next("expr");
-                                println!(
-                                    "    if let Guard::IfLet(ref {}, ref {}) = {};",
-                                    if_let_pat_pat, if_let_expr_pat, guard_pat
-                                );
-                                self.current = if_let_expr_pat;
-                                self.visit_expr(if_let_expr);
-                                self.current = if_let_pat_pat;
-                                self.visit_pat(if_let_pat);
-                            },
-                        }
-                    }
-                    self.current = format!("{}[{}].pat", arms_pat, i);
-                    self.visit_pat(arm.pat);
-                }
-            },
-            ExprKind::Closure(ref _capture_clause, _func, _, _, _) => {
-                println!("Closure(ref capture_clause, ref func, _, _, _) = {};", current);
-                println!("    // unimplemented: `ExprKind::Closure` is not further destructured at the moment");
-            },
-            ExprKind::Yield(sub, _) => {
-                let sub_pat = self.next("sub");
-                println!("Yield(ref sub) = {};", current);
-                self.current = sub_pat;
-                self.visit_expr(sub);
-            },
-            ExprKind::Block(block, _) => {
-                let block_pat = self.next("block");
-                println!("Block(ref {}) = {};", block_pat, current);
-                self.current = block_pat;
-                self.visit_block(block);
+                bind!(self, expr);
+                kind!("Type({expr}, _)");
+                self.expr(expr);
+            },
+            ExprKind::Loop(body, label, des, _) => {
+                bind!(self, body);
+                opt_bind!(self, label);
+                kind!("Loop({body}, {label}, LoopSource::{des:?}, _)");
+                self.block(body);
+                label.if_some(|l| self.ident(field!(l.ident)));
+            },
+            ExprKind::If(cond, then, else_expr) => {
+                bind!(self, cond, then);
+                opt_bind!(self, else_expr);
+                kind!("If({cond}, {then}, {else_expr})");
+                self.expr(cond);
+                self.expr(then);
+                else_expr.if_some(|e| self.expr(e));
+            },
+            ExprKind::Match(scrutinee, arms, des) => {
+                bind!(self, scrutinee, arms);
+                kind!("Match({scrutinee}, {arms}, MatchSource::{des:?})");
+                self.expr(scrutinee);
+                self.slice(arms, |arm| self.arm(arm));
+            },
+            ExprKind::Closure(capture_by, fn_decl, body_id, _, movability) => {
+                let movability = OptionPat::new(movability.map(|m| format!("Movability::{m:?}")));
+
+                let ret_ty = match fn_decl.output {
+                    FnRetTy::DefaultReturn(_) => "FnRetTy::DefaultReturn(_)",
+                    FnRetTy::Return(_) => "FnRetTy::Return(_ty)",
+                };
+
+                bind!(self, fn_decl, body_id);
+                kind!("Closure(CaptureBy::{capture_by:?}, {fn_decl}, {body_id}, _, {movability})");
+                out!("if let {ret_ty} = {fn_decl}.output;");
+                self.body(body_id);
+            },
+            ExprKind::Yield(sub, source) => {
+                bind!(self, sub);
+                kind!("Yield(sub, YieldSource::{source:?})");
+                self.expr(sub);
+            },
+            ExprKind::Block(block, label) => {
+                bind!(self, block);
+                opt_bind!(self, label);
+                kind!("Block({block}, {label})");
+                self.block(block);
+                label.if_some(|l| self.ident(field!(l.ident)));
             },
             ExprKind::Assign(target, value, _) => {
-                let target_pat = self.next("target");
-                let value_pat = self.next("value");
-                println!(
-                    "Assign(ref {}, ref {}, ref _span) = {};",
-                    target_pat, value_pat, current
-                );
-                self.current = target_pat;
-                self.visit_expr(target);
-                self.current = value_pat;
-                self.visit_expr(value);
-            },
-            ExprKind::AssignOp(ref op, target, value) => {
-                let op_pat = self.next("op");
-                let target_pat = self.next("target");
-                let value_pat = self.next("value");
-                println!(
-                    "AssignOp(ref {}, ref {}, ref {}) = {};",
-                    op_pat, target_pat, value_pat, current
-                );
-                println!("    if BinOpKind::{:?} == {}.node;", op.node, op_pat);
-                self.current = target_pat;
-                self.visit_expr(target);
-                self.current = value_pat;
-                self.visit_expr(value);
-            },
-            ExprKind::Field(object, ref field_ident) => {
-                let obj_pat = self.next("object");
-                let field_name_pat = self.next("field_name");
-                println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current);
-                println!("    if {}.as_str() == {:?}", field_name_pat, field_ident.as_str());
-                self.current = obj_pat;
-                self.visit_expr(object);
+                bind!(self, target, value);
+                kind!("Assign({target}, {value}, _span)");
+                self.expr(target);
+                self.expr(value);
+            },
+            ExprKind::AssignOp(op, target, value) => {
+                bind!(self, op, target, value);
+                kind!("AssignOp({op}, {target}, {value})");
+                out!("if BinOpKind::{:?} == {op}.node;", op.value.node);
+                self.expr(target);
+                self.expr(value);
+            },
+            ExprKind::Field(object, field_name) => {
+                bind!(self, object, field_name);
+                kind!("Field({object}, {field_name})");
+                self.ident(field_name);
+                self.expr(object);
             },
             ExprKind::Index(object, index) => {
-                let object_pat = self.next("object");
-                let index_pat = self.next("index");
-                println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current);
-                self.current = object_pat;
-                self.visit_expr(object);
-                self.current = index_pat;
-                self.visit_expr(index);
-            },
-            ExprKind::Path(ref path) => {
-                let path_pat = self.next("path");
-                println!("Path(ref {}) = {};", path_pat, current);
-                self.current = path_pat;
-                self.print_qpath(path);
+                bind!(self, object, index);
+                kind!("Index({object}, {index})");
+                self.expr(object);
+                self.expr(index);
+            },
+            ExprKind::Path(ref qpath) => {
+                bind!(self, qpath);
+                kind!("Path(ref {qpath})");
+                self.qpath(qpath);
             },
             ExprKind::AddrOf(kind, mutability, inner) => {
-                let inner_pat = self.next("inner");
-                println!(
-                    "AddrOf(BorrowKind::{:?}, Mutability::{:?}, ref {}) = {};",
-                    kind, mutability, inner_pat, current
-                );
-                self.current = inner_pat;
-                self.visit_expr(inner);
-            },
-            ExprKind::Break(ref _destination, ref opt_value) => {
-                let destination_pat = self.next("destination");
-                if let Some(value) = *opt_value {
-                    let value_pat = self.next("value");
-                    println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current);
-                    self.current = value_pat;
-                    self.visit_expr(value);
-                } else {
-                    println!("Break(ref {}, None) = {};", destination_pat, current);
-                }
-                // FIXME: implement label printing
-            },
-            ExprKind::Continue(ref _destination) => {
-                let destination_pat = self.next("destination");
-                println!("Again(ref {}) = {};", destination_pat, current);
-                // FIXME: implement label printing
-            },
-            ExprKind::Ret(ref opt_value) => {
-                if let Some(value) = *opt_value {
-                    let value_pat = self.next("value");
-                    println!("Ret(Some(ref {})) = {};", value_pat, current);
-                    self.current = value_pat;
-                    self.visit_expr(value);
-                } else {
-                    println!("Ret(None) = {};", current);
-                }
+                bind!(self, inner);
+                kind!("AddrOf(BorrowKind::{kind:?}, Mutability::{mutability:?}, {inner})");
+                self.expr(inner);
+            },
+            ExprKind::Break(destination, value) => {
+                bind!(self, destination);
+                opt_bind!(self, value);
+                kind!("Break({destination}, {value})");
+                self.destination(destination);
+                value.if_some(|e| self.expr(e));
+            },
+            ExprKind::Continue(destination) => {
+                bind!(self, destination);
+                kind!("Continue({destination})");
+                self.destination(destination);
+            },
+            ExprKind::Ret(value) => {
+                opt_bind!(self, value);
+                kind!("Ret({value})");
+                value.if_some(|e| self.expr(e));
             },
             ExprKind::InlineAsm(_) => {
-                println!("InlineAsm(_) = {};", current);
-                println!("    // unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment");
+                kind!("InlineAsm(_)");
+                out!("// unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment");
             },
             ExprKind::LlvmInlineAsm(_) => {
-                println!("LlvmInlineAsm(_) = {};", current);
-                println!("    // unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment");
-            },
-            ExprKind::Struct(path, fields, ref opt_base) => {
-                let path_pat = self.next("path");
-                let fields_pat = self.next("fields");
-                if let Some(base) = *opt_base {
-                    let base_pat = self.next("base");
-                    println!(
-                        "Struct(ref {}, ref {}, Some(ref {})) = {};",
-                        path_pat, fields_pat, base_pat, current
-                    );
-                    self.current = base_pat;
-                    self.visit_expr(base);
-                } else {
-                    println!("Struct(ref {}, ref {}, None) = {};", path_pat, fields_pat, current);
-                }
-                self.current = path_pat;
-                self.print_qpath(path);
-                println!("    if {}.len() == {};", fields_pat, fields.len());
-                println!("    // unimplemented: field checks");
-            },
-            ExprKind::ConstBlock(_) => {
-                let value_pat = self.next("value");
-                println!("Const({})", value_pat);
-                self.current = value_pat;
-            },
-            // FIXME: compute length (needs type info)
-            ExprKind::Repeat(value, _) => {
-                let value_pat = self.next("value");
-                println!("Repeat(ref {}, _) = {};", value_pat, current);
-                println!("// unimplemented: repeat count check");
-                self.current = value_pat;
-                self.visit_expr(value);
-            },
-            ExprKind::Err => {
-                println!("Err = {}", current);
-            },
+                kind!("LlvmInlineAsm(_)");
+                out!("// unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment");
+            },
+            ExprKind::Struct(qpath, fields, base) => {
+                bind!(self, qpath, fields);
+                opt_bind!(self, base);
+                kind!("Struct({qpath}, {fields}, {base})");
+                self.qpath(qpath);
+                self.slice(fields, |field| {
+                    self.ident(field!(field.ident));
+                    self.expr(field!(field.expr));
+                });
+                base.if_some(|e| self.expr(e));
+            },
+            ExprKind::ConstBlock(_) => kind!("ConstBlock(_)"),
+            ExprKind::Repeat(value, length) => {
+                bind!(self, value, length);
+                kind!("Repeat({value}, {length})");
+                self.expr(value);
+                self.body(field!(length.body));
+            },
+            ExprKind::Err => kind!("Err"),
             ExprKind::DropTemps(expr) => {
-                let expr_pat = self.next("expr");
-                println!("DropTemps(ref {}) = {};", expr_pat, current);
-                self.current = expr_pat;
-                self.visit_expr(expr);
+                bind!(self, expr);
+                kind!("DropTemps({expr})");
+                self.expr(expr);
             },
         }
     }
 
-    fn visit_block(&mut self, block: &Block<'_>) {
-        println!("    if {}.stmts.len() == {};", self.current, block.stmts.len());
-        let block_name = self.current.clone();
-        for (i, stmt) in block.stmts.iter().enumerate() {
-            self.current = format!("{}.stmts[{}]", block_name, i);
-            self.visit_stmt(stmt);
-        }
-        if let Some(expr) = block.expr {
-            self.current = self.next("trailing_expr");
-            println!("    if let Some({}) = &{}.expr;", self.current, block_name);
-            self.visit_expr(expr);
-        } else {
-            println!("    if {}.expr.is_none();", block_name);
-        }
+    fn block(&self, block: &Binding<&hir::Block<'_>>) {
+        self.slice(field!(block.stmts), |stmt| self.stmt(stmt));
+        self.option(field!(block.expr), "trailing_expr", |expr| {
+            self.expr(expr);
+        });
     }
 
-    #[allow(clippy::too_many_lines)]
-    fn visit_pat(&mut self, pat: &Pat<'_>) {
-        print!("    if let PatKind::");
-        let current = format!("{}.kind", self.current);
-        match pat.kind {
-            PatKind::Wild => println!("Wild = {};", current),
-            PatKind::Binding(anno, .., ident, ref sub) => {
-                let anno_pat = &format!("BindingAnnotation::{:?}", anno);
-                let name_pat = self.next("name");
-                if let Some(sub) = *sub {
-                    let sub_pat = self.next("sub");
-                    println!(
-                        "Binding({}, _, {}, Some(ref {})) = {};",
-                        anno_pat, name_pat, sub_pat, current
-                    );
-                    self.current = sub_pat;
-                    self.visit_pat(sub);
-                } else {
-                    println!("Binding({}, _, {}, None) = {};", anno_pat, name_pat, current);
-                }
-                println!("    if {}.as_str() == \"{}\";", name_pat, ident.as_str());
-            },
-            PatKind::Struct(ref path, fields, ignore) => {
-                let path_pat = self.next("path");
-                let fields_pat = self.next("fields");
-                println!(
-                    "Struct(ref {}, ref {}, {}) = {};",
-                    path_pat, fields_pat, ignore, current
-                );
-                self.current = path_pat;
-                self.print_qpath(path);
-                println!("    if {}.len() == {};", fields_pat, fields.len());
-                println!("    // unimplemented: field checks");
+    fn body(&self, body_id: &Binding<hir::BodyId>) {
+        let expr = &self.cx.tcx.hir().body(body_id.value).value;
+        bind!(self, expr);
+        out!("let {expr} = &cx.tcx.hir().body({body_id}).value;");
+        self.expr(expr);
+    }
+
+    fn pat(&self, pat: &Binding<&hir::Pat<'_>>) {
+        let kind = |kind| out!("if let PatKind::{kind} = {pat}.kind;");
+        macro_rules! kind {
+            ($($t:tt)*) => (kind(format_args!($($t)*)));
+        }
+
+        match pat.value.kind {
+            PatKind::Wild => kind!("Wild"),
+            PatKind::Binding(anno, .., name, sub) => {
+                bind!(self, name);
+                opt_bind!(self, sub);
+                kind!("Binding(BindingAnnotation::{anno:?}, _, {name}, {sub})");
+                self.ident(name);
+                sub.if_some(|p| self.pat(p));
+            },
+            PatKind::Struct(ref qpath, fields, ignore) => {
+                bind!(self, qpath, fields);
+                kind!("Struct(ref {qpath}, {fields}, {ignore})");
+                self.qpath(qpath);
+                self.slice(fields, |field| {
+                    self.ident(field!(field.ident));
+                    self.pat(field!(field.pat));
+                });
             },
             PatKind::Or(fields) => {
-                let fields_pat = self.next("fields");
-                println!("Or(ref {}) = {};", fields_pat, current);
-                println!("    if {}.len() == {};", fields_pat, fields.len());
-                println!("    // unimplemented: field checks");
-            },
-            PatKind::TupleStruct(ref path, fields, skip_pos) => {
-                let path_pat = self.next("path");
-                let fields_pat = self.next("fields");
-                println!(
-                    "TupleStruct(ref {}, ref {}, {:?}) = {};",
-                    path_pat, fields_pat, skip_pos, current
-                );
-                self.current = path_pat;
-                self.print_qpath(path);
-                println!("    if {}.len() == {};", fields_pat, fields.len());
-                println!("    // unimplemented: field checks");
-            },
-            PatKind::Path(ref path) => {
-                let path_pat = self.next("path");
-                println!("Path(ref {}) = {};", path_pat, current);
-                self.current = path_pat;
-                self.print_qpath(path);
+                bind!(self, fields);
+                kind!("Or({fields})");
+                self.slice(fields, |pat| self.pat(pat));
+            },
+            PatKind::TupleStruct(ref qpath, fields, skip_pos) => {
+                bind!(self, qpath, fields);
+                kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})");
+                self.qpath(qpath);
+                self.slice(fields, |pat| self.pat(pat));
+            },
+            PatKind::Path(ref qpath) => {
+                bind!(self, qpath);
+                kind!("Path(ref {qpath})");
+                self.qpath(qpath);
             },
             PatKind::Tuple(fields, skip_pos) => {
-                let fields_pat = self.next("fields");
-                println!("Tuple(ref {}, {:?}) = {};", fields_pat, skip_pos, current);
-                println!("    if {}.len() == {};", fields_pat, fields.len());
-                println!("    // unimplemented: field checks");
+                bind!(self, fields);
+                kind!("Tuple({fields}, {skip_pos:?})");
+                self.slice(fields, |field| self.pat(field));
             },
             PatKind::Box(pat) => {
-                let pat_pat = self.next("pat");
-                println!("Box(ref {}) = {};", pat_pat, current);
-                self.current = pat_pat;
-                self.visit_pat(pat);
+                bind!(self, pat);
+                kind!("Box({pat})");
+                self.pat(pat);
             },
             PatKind::Ref(pat, muta) => {
-                let pat_pat = self.next("pat");
-                println!("Ref(ref {}, Mutability::{:?}) = {};", pat_pat, muta, current);
-                self.current = pat_pat;
-                self.visit_pat(pat);
+                bind!(self, pat);
+                kind!("Ref({pat}, Mutability::{muta:?})");
+                self.pat(pat);
             },
             PatKind::Lit(lit_expr) => {
-                let lit_expr_pat = self.next("lit_expr");
-                println!("Lit(ref {}) = {}", lit_expr_pat, current);
-                self.current = lit_expr_pat;
-                self.visit_expr(lit_expr);
-            },
-            PatKind::Range(ref start, ref end, end_kind) => {
-                let start_pat = self.next("start");
-                let end_pat = self.next("end");
-                println!(
-                    "Range(ref {}, ref {}, RangeEnd::{:?}) = {};",
-                    start_pat, end_pat, end_kind, current
-                );
-                self.current = start_pat;
-                walk_list!(self, visit_expr, start);
-                self.current = end_pat;
-                walk_list!(self, visit_expr, end);
-            },
-            PatKind::Slice(start, ref middle, end) => {
-                let start_pat = self.next("start");
-                let end_pat = self.next("end");
-                if let Some(middle) = middle {
-                    let middle_pat = self.next("middle");
-                    println!(
-                        "Slice(ref {}, Some(ref {}), ref {}) = {};",
-                        start_pat, middle_pat, end_pat, current
-                    );
-                    self.current = middle_pat;
-                    self.visit_pat(middle);
-                } else {
-                    println!("Slice(ref {}, None, ref {}) = {};", start_pat, end_pat, current);
-                }
-                println!("    if {}.len() == {};", start_pat, start.len());
-                for (i, pat) in start.iter().enumerate() {
-                    self.current = format!("{}[{}]", start_pat, i);
-                    self.visit_pat(pat);
-                }
-                println!("    if {}.len() == {};", end_pat, end.len());
-                for (i, pat) in end.iter().enumerate() {
-                    self.current = format!("{}[{}]", end_pat, i);
-                    self.visit_pat(pat);
-                }
+                bind!(self, lit_expr);
+                kind!("Lit({lit_expr})");
+                self.expr(lit_expr);
+            },
+            PatKind::Range(start, end, end_kind) => {
+                opt_bind!(self, start, end);
+                kind!("Range({start}, {end}, RangeEnd::{end_kind:?})");
+                start.if_some(|e| self.expr(e));
+                end.if_some(|e| self.expr(e));
+            },
+            PatKind::Slice(start, middle, end) => {
+                bind!(self, start, end);
+                opt_bind!(self, middle);
+                kind!("Slice({start}, {middle}, {end})");
+                middle.if_some(|p| self.pat(p));
+                self.slice(start, |pat| self.pat(pat));
+                self.slice(end, |pat| self.pat(pat));
             },
         }
     }
 
-    fn visit_stmt(&mut self, s: &Stmt<'_>) {
-        print!("    if let StmtKind::");
-        let current = format!("{}.kind", self.current);
-        match s.kind {
-            // A local (let) binding:
-            StmtKind::Local(local) => {
-                let local_pat = self.next("local");
-                println!("Local(ref {}) = {};", local_pat, current);
-                if let Some(init) = local.init {
-                    let init_pat = self.next("init");
-                    println!("    if let Some(ref {}) = {}.init;", init_pat, local_pat);
-                    self.current = init_pat;
-                    self.visit_expr(init);
-                }
-                self.current = format!("{}.pat", local_pat);
-                self.visit_pat(local.pat);
-            },
-            // An item binding:
-            StmtKind::Item(_) => {
-                println!("Item(item_id) = {};", current);
-            },
+    fn stmt(&self, stmt: &Binding<&hir::Stmt<'_>>) {
+        let kind = |kind| out!("if let StmtKind::{kind} = {stmt}.kind;");
+        macro_rules! kind {
+            ($($t:tt)*) => (kind(format_args!($($t)*)));
+        }
 
-            // Expr without trailing semi-colon (must have unit type):
+        match stmt.value.kind {
+            StmtKind::Local(local) => {
+                bind!(self, local);
+                kind!("Local({local})");
+                self.option(field!(local.init), "init", |init| {
+                    self.expr(init);
+                });
+                self.pat(field!(local.pat));
+            },
+            StmtKind::Item(_) => kind!("Item(item_id)"),
             StmtKind::Expr(e) => {
-                let e_pat = self.next("e");
-                println!("Expr(ref {}, _) = {}", e_pat, current);
-                self.current = e_pat;
-                self.visit_expr(e);
+                bind!(self, e);
+                kind!("Expr({e})");
+                self.expr(e);
             },
-
-            // Expr with trailing semi-colon (may have any type):
             StmtKind::Semi(e) => {
-                let e_pat = self.next("e");
-                println!("Semi(ref {}, _) = {}", e_pat, current);
-                self.current = e_pat;
-                self.visit_expr(e);
+                bind!(self, e);
+                kind!("Semi({e})");
+                self.expr(e);
             },
         }
     }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
 }
 
 fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
@@ -741,30 +697,29 @@ fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
     get_attr(cx.sess(), attrs, "author").count() > 0
 }
 
-fn print_path(path: &QPath<'_>, first: &mut bool) {
-    match *path {
-        QPath::Resolved(_, path) => {
-            for segment in path.segments {
-                if *first {
-                    *first = false;
-                } else {
-                    print!(", ");
-                }
-                print!("{:?}", segment.ident.as_str());
-            }
-        },
-        QPath::TypeRelative(ty, segment) => match ty.kind {
-            hir::TyKind::Path(ref inner_path) => {
-                print_path(inner_path, first);
-                if *first {
-                    *first = false;
-                } else {
-                    print!(", ");
+fn path_to_string(path: &QPath<'_>) -> String {
+    fn inner(s: &mut String, path: &QPath<'_>) {
+        match *path {
+            QPath::Resolved(_, path) => {
+                for (i, segment) in path.segments.iter().enumerate() {
+                    if i > 0 {
+                        *s += ", ";
+                    }
+                    write!(s, "{:?}", segment.ident.as_str()).unwrap();
                 }
-                print!("{:?}", segment.ident.as_str());
             },
-            ref other => print!("/* unimplemented: {:?}*/", other),
-        },
-        QPath::LangItem(..) => panic!("print_path: called for lang item qpath"),
+            QPath::TypeRelative(ty, segment) => match &ty.kind {
+                hir::TyKind::Path(inner_path) => {
+                    inner(s, inner_path);
+                    *s += ", ";
+                    write!(s, "{:?}", segment.ident.as_str()).unwrap();
+                },
+                other => write!(s, "/* unimplemented: {:?}*/", other).unwrap(),
+            },
+            QPath::LangItem(..) => panic!("path_to_string: called for lang item qpath"),
+        }
     }
+    let mut s = String::new();
+    inner(&mut s, path);
+    s
 }
index 122a5ce3fc8f12d19fd8d0ce877ff2a672c01537..9c83d30eb9cc1a0a691810ff7568a5a6c50bff31 100644 (file)
@@ -15,7 +15,7 @@ pub struct Rename {
     pub rename: String,
 }
 
-/// A single disallowed method, used by the `DISALLOWED_METHOD` lint.
+/// A single disallowed method, used by the `DISALLOWED_METHODS` lint.
 #[derive(Clone, Debug, Deserialize)]
 #[serde(untagged)]
 pub enum DisallowedMethod {
@@ -23,7 +23,7 @@ pub enum DisallowedMethod {
     WithReason { path: String, reason: Option<String> },
 }
 
-/// A single disallowed type, used by the `DISALLOWED_TYPE` lint.
+/// A single disallowed type, used by the `DISALLOWED_TYPES` lint.
 #[derive(Clone, Debug, Deserialize)]
 #[serde(untagged)]
 pub enum DisallowedType {
@@ -148,7 +148,7 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
     ///
     /// Suppress lints whenever the suggested change would cause breakage for other crates.
     (avoid_breaking_exported_api: bool = true),
-    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT.
+    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE.
     ///
     /// The minimum rust version that the project supports
     (msrv: Option<String> = None),
@@ -256,11 +256,11 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
     ///
     /// Whether to allow certain wildcard imports (prelude, super in tests).
     (warn_on_all_wildcard_imports: bool = false),
-    /// Lint: DISALLOWED_METHOD.
+    /// Lint: DISALLOWED_METHODS.
     ///
     /// The list of disallowed methods, written as fully qualified paths.
     (disallowed_methods: Vec<crate::utils::conf::DisallowedMethod> = Vec::new()),
-    /// Lint: DISALLOWED_TYPE.
+    /// Lint: DISALLOWED_TYPES.
     ///
     /// The list of disallowed types, written as fully qualified paths.
     (disallowed_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
@@ -296,6 +296,12 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
     ///
     /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
     (enable_raw_pointer_heuristic_for_send: bool = true),
+    /// Lint: INDEX_REFUTABLE_SLICE.
+    ///
+    /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
+    /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
+    /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
+    (max_suggested_slice_pattern_length: u64 = 3),
 }
 
 /// Search for the configuration file.
index 824ec53ab9c75301f822805eaf5cf860fe03ff74..1f97c8ba7e62cbba28398aa3e43624af357e18ba 100644 (file)
@@ -8,6 +8,7 @@
     paths, SpanlessEq,
 };
 use if_chain::if_chain;
+use rustc_ast as ast;
 use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId};
 use rustc_ast::visit::FnKind;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_middle::hir::map::Map;
 use rustc_middle::mir::interpret::ConstValue;
 use rustc_middle::ty;
+use rustc_semver::RustcVersion;
 use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{Symbol, SymbolStr};
-use rustc_span::{BytePos, Span};
+use rustc_span::{sym, BytePos, Span};
 use rustc_typeck::hir_ty_to_ty;
 
 use std::borrow::{Borrow, Cow};
     "non-idiomatic `if_chain!` usage"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for invalid `clippy::version` attributes.
+    ///
+    /// Valid values are:
+    /// * "pre 1.29.0"
+    /// * any valid semantic version
+    pub INVALID_CLIPPY_VERSION_ATTRIBUTE,
+    internal,
+    "found an invalid `clippy::version` attribute"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for declared clippy lints without the `clippy::version` attribute.
+    ///
+    pub MISSING_CLIPPY_VERSION_ATTRIBUTE,
+    internal,
+    "found clippy lint without `clippy::version` attribute"
+}
+
 declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
 
 impl EarlyLintPass for ClippyLintsInternal {
@@ -351,7 +374,7 @@ pub struct LintWithoutLintPass {
     registered_lints: FxHashSet<Symbol>,
 }
 
-impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]);
+impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]);
 
 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@@ -361,6 +384,8 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 
         if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
             if is_lint_ref_type(cx, ty) {
+                check_invalid_clippy_version_attribute(cx, item);
+
                 let expr = &cx.tcx.hir().body(body_id).value;
                 if_chain! {
                     if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind;
@@ -458,6 +483,57 @@ fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool {
     false
 }
 
+fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
+    if let Some(value) = extract_clippy_version_value(cx, item) {
+        // The `sym!` macro doesn't work as it only expects a single token.
+        // It's better to keep it this way and have a direct `Symbol::intern` call here.
+        if value == Symbol::intern("pre 1.29.0") {
+            return;
+        }
+
+        if RustcVersion::parse(&*value.as_str()).is_err() {
+            span_lint_and_help(
+                cx,
+                INVALID_CLIPPY_VERSION_ATTRIBUTE,
+                item.span,
+                "this item has an invalid `clippy::version` attribute",
+                None,
+                "please use a valid sematic version, see `doc/adding_lints.md`",
+            );
+        }
+    } else {
+        span_lint_and_help(
+            cx,
+            MISSING_CLIPPY_VERSION_ATTRIBUTE,
+            item.span,
+            "this lint is missing the `clippy::version` attribute or version value",
+            None,
+            "please use a `clippy::version` attribute, see `doc/adding_lints.md`",
+        );
+    }
+}
+
+/// This function extracts the version value of a `clippy::version` attribute if the given value has
+/// one
+fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
+    let attrs = cx.tcx.hir().attrs(item.hir_id());
+    attrs.iter().find_map(|attr| {
+        if_chain! {
+            // Identify attribute
+            if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind;
+            if let [tool_name, attr_name] = &attr_kind.path.segments[..];
+            if tool_name.ident.name == sym::clippy;
+            if attr_name.ident.name == sym::version;
+            if let Some(version) = attr.value_str();
+            then {
+                Some(version)
+            } else {
+                None
+            }
+        }
+    })
+}
+
 struct LintCollector<'a, 'tcx> {
     output: &'a mut FxHashSet<Symbol>,
     cx: &'a LateContext<'tcx>,
index 99cf4c1ed40fbcbd0a52105176f455b05f230995..8051c58bad7a2711345098c08abcfa3f873df494 100644 (file)
@@ -25,7 +25,7 @@
 use std::io::prelude::*;
 use std::path::Path;
 
-use crate::utils::internal_lints::is_lint_ref_type;
+use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
 use clippy_utils::{
     diagnostics::span_lint, last_path_segment, match_def_path, match_function_call, match_path, paths, ty::match_type,
     ty::walk_ptrs_ty_depth,
@@ -116,6 +116,8 @@ macro_rules! CONFIGURATION_VALUE_TEMPLATE {
 const APPLICABILITY_NAME_INDEX: usize = 2;
 /// This applicability will be set for unresolved applicability values.
 const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
+/// The version that will be displayed if none has been defined
+const VERION_DEFAULT_STR: &str = "Unknown";
 
 declare_clippy_lint! {
     /// ### What it does
@@ -144,6 +146,7 @@ macro_rules! CONFIGURATION_VALUE_TEMPLATE {
     ///     "docs": " ### What it does\nCollects metadata about clippy lints for the website. [...] "
     /// }
     /// ```
+    #[clippy::version = "1.56.0"]
     pub INTERNAL_METADATA_COLLECTOR,
     internal_warn,
     "A busy bee collection metadata about lints"
@@ -215,18 +218,27 @@ struct LintMetadata {
     group: String,
     level: String,
     docs: String,
+    version: String,
     /// This field is only used in the output and will only be
     /// mapped shortly before the actual output.
     applicability: Option<ApplicabilityInfo>,
 }
 
 impl LintMetadata {
-    fn new(id: String, id_span: SerializableSpan, group: String, level: &'static str, docs: String) -> Self {
+    fn new(
+        id: String,
+        id_span: SerializableSpan,
+        group: String,
+        level: &'static str,
+        version: String,
+        docs: String,
+    ) -> Self {
         Self {
             id,
             id_span,
             group,
             level: level.to_string(),
+            version,
             docs,
             applicability: None,
         }
@@ -410,12 +422,14 @@ fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
                     if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
                         docs.push_str(&configuration_section);
                     }
+                    let version = get_lint_version(cx, item);
 
                     self.lints.push(LintMetadata::new(
                         lint_name,
                         SerializableSpan::from_item(cx, item),
                         group,
                         level,
+                        version,
                         docs,
                     ));
                 }
@@ -429,11 +443,14 @@ fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
                 // Metadata the little we can get from a deprecated lint
                 if let Some(docs) = extract_attr_docs_or_lint(cx, item);
                 then {
+                    let version = get_lint_version(cx, item);
+
                     self.lints.push(LintMetadata::new(
                         lint_name,
                         SerializableSpan::from_item(cx, item),
                         DEPRECATED_LINT_GROUP_STR.to_string(),
                         DEPRECATED_LINT_LEVEL,
+                        version,
                         docs,
                     ));
                 }
@@ -552,6 +569,13 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
     Some(docs)
 }
 
+fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
+    extract_clippy_version_value(cx, item).map_or_else(
+        || VERION_DEFAULT_STR.to_string(),
+        |version| version.as_str().to_string(),
+    )
+}
+
 fn get_lint_group_and_level_or_lint(
     cx: &LateContext<'_>,
     lint_name: &str,
@@ -663,7 +687,6 @@ fn extract_emission_info<'hir>(
             applicability = resolve_applicability(cx, arg);
         } else if arg_ty.is_closure() {
             multi_part |= check_is_multi_part(cx, arg);
-            // TODO xFrednet 2021-03-01: don't use or_else but rather a comparison
             applicability = applicability.or_else(|| resolve_applicability(cx, arg));
         }
     }
index d3234b5758a575b2066f57cb673959ef5420ed3b..79e7410c3a8381183f256da06d11fb7a446db435 100644 (file)
@@ -36,6 +36,7 @@ pub struct UselessVec {
     /// // Good
     /// foo(&[1, 2]);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_VEC,
     perf,
     "useless `vec!`"
index b92b6ca4f4380f981ee9f9ed163d44470994555c..1bc0eb6303c01f692c04d08fdf29f94e86fecf0a 100644 (file)
@@ -27,6 +27,7 @@
     /// ```rust
     /// let v = vec![0];
     /// ```
+    #[clippy::version = "1.51.0"]
     pub VEC_INIT_THEN_PUSH,
     perf,
     "`push` immediately after `Vec` creation"
index 5c0429db6b8dfe5ba6c988675db17b8b2268f6e8..3441d9ccdfa25db333ee7a22456b3eb952ddcb4e 100644 (file)
@@ -20,6 +20,7 @@
     /// ```rust
     /// vec!(1, 2, 3, 4, 5).resize(0, 5)
     /// ```
+    #[clippy::version = "1.46.0"]
     pub VEC_RESIZE_TO_ZERO,
     correctness,
     "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
index e07c12f4f16159d16abc6814ca96998d16f15feb..ebdaff1e676b7e187511536f6dca84bc38f58804 100644 (file)
@@ -27,6 +27,7 @@
     /// # use std::fs;
     /// let mut bytes = fs::read("foo.txt").unwrap();
     /// ```
+    #[clippy::version = "1.44.0"]
     pub VERBOSE_FILE_READS,
     restriction,
     "use of `File::read_to_end` or `File::read_to_string`"
index d0c98b6bd798a5a368ef7b1fcb2427bf044c44f5..80d7b8a1b6df559820863c39c0199f9abb88615c 100644 (file)
@@ -20,6 +20,7 @@
     /// [dependencies]
     /// regex = "*"
     /// ```
+    #[clippy::version = "1.32.0"]
     pub WILDCARD_DEPENDENCIES,
     cargo,
     "wildcard dependencies being used"
index bafb9d3e3b16a97bdb62f0d6ab73a9988e99b2a2..832da66a53695c55287ddda2eb3dc1343cab1b0a 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_test_module_or_function;
 use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::{in_macro, is_test_module_or_function};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{
@@ -34,6 +34,7 @@
     /// use std::cmp::Ordering;
     /// foo(Ordering::Less)
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ENUM_GLOB_USE,
     pedantic,
     "use items that import all variants of an enum"
@@ -86,6 +87,7 @@
     ///
     /// foo();
     /// ```
+    #[clippy::version = "1.43.0"]
     pub WILDCARD_IMPORTS,
     pedantic,
     "lint `use _::*` statements"
@@ -196,7 +198,7 @@ fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
 
 impl WildcardImports {
     fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool {
-        in_macro(item.span)
+        item.span.from_expansion()
             || is_prelude_import(segments)
             || (is_super_only_import(segments) && self.test_modules_deep > 0)
     }
index b412e15ae4f82c9b474f71837b84cb944ebbd23e..5bf0cffdbad16a52983d0f7f48e2ad476c4cd8e4 100644 (file)
@@ -31,6 +31,7 @@
     /// // Good
     /// println!();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PRINTLN_EMPTY_STRING,
     style,
     "using `println!(\"\")` with an empty string"
@@ -55,6 +56,7 @@
     /// # let name = "World";
     /// println!("Hello {}!", name);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PRINT_WITH_NEWLINE,
     style,
     "using `print!()` with a format string that ends in a single newline"
     /// application and might forget to remove those prints afterward.
     ///
     /// ### Known problems
-    /// Only catches `print!` and `println!` calls.
+    /// * Only catches `print!` and `println!` calls.
+    /// * The lint level is unaffected by crate attributes. The level can still
+    ///   be set for functions, modules and other items. To change the level for
+    ///   the entire crate, please use command line flags. More information and a
+    ///   configuration example can be found in [clippy#6610].
+    ///
+    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
     ///
     /// ### Example
     /// ```rust
     /// println!("Hello world!");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PRINT_STDOUT,
     restriction,
     "printing on stdout"
     /// application and might forget to remove those prints afterward.
     ///
     /// ### Known problems
-    /// Only catches `eprint!` and `eprintln!` calls.
+    /// * Only catches `eprint!` and `eprintln!` calls.
+    /// * The lint level is unaffected by crate attributes. The level can still
+    ///   be set for functions, modules and other items. To change the level for
+    ///   the entire crate, please use command line flags. More information and a
+    ///   configuration example can be found in [clippy#6610].
+    ///
+    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
     ///
     /// ### Example
     /// ```rust
     /// eprintln!("Hello world!");
     /// ```
+    #[clippy::version = "1.50.0"]
     pub PRINT_STDERR,
     restriction,
     "printing on stderr"
     /// # let foo = "bar";
     /// println!("{:?}", foo);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USE_DEBUG,
     restriction,
     "use of `Debug`-based formatting"
     /// ```rust
     /// println!("foo");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PRINT_LITERAL,
     style,
     "printing a literal with a format string"
     /// // Good
     /// writeln!(buf);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRITELN_EMPTY_STRING,
     style,
     "using `writeln!(buf, \"\")` with an empty string"
     /// // Good
     /// writeln!(buf, "Hello {}!", name);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRITE_WITH_NEWLINE,
     style,
     "using `write!()` with a format string that ends in a single newline"
     /// // Good
     /// writeln!(buf, "foo");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRITE_LITERAL,
     style,
     "writing a literal with a format string"
@@ -562,10 +583,10 @@ fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool)
             let replacement: String = match lit.token.kind {
                 LitKind::Integer | LitKind::Float | LitKind::Err => continue,
                 LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => {
-                    lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}")
+                    lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
                 },
                 LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
-                    lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}")
+                    lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
                 },
                 LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue,
                 LitKind::Byte | LitKind::Char => match &*lit.token.symbol.as_str() {
index e0746ce4d8121b115f1da360d5b2e9756aaa3c06..641681185a2f60a7ad48d672690b88786f2d8ca5 100644 (file)
@@ -20,6 +20,7 @@
     /// // Good
     /// let nan = f32::NAN;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ZERO_DIVIDED_BY_ZERO,
     complexity,
     "usage of `0.0 / 0.0` to obtain NaN instead of `f32::NAN` or `f64::NAN`"
index aa6b2614bbc908ca6e5f7757acfcc30f22348d15..eb8436a501d54b057010db4c95b6f67a68a9452e 100644 (file)
@@ -36,6 +36,7 @@
     ///     todo!();
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub ZERO_SIZED_MAP_VALUES,
     pedantic,
     "usage of map with zero-sized value type"
index d99a3d9359e1f439ba973431874f2b2b3c3941d1..0ba0b59ed13daf6f93653f554b03f6a4d0a0076f 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_utils"
-version = "0.1.58"
+version = "0.1.59"
 edition = "2021"
 publish = false
 
index 1b05a8a35046ede0d99e4a5b439f52659ab2a075..8400bfbbd99d2550d09021754a61ffc8e4cf5f9b 100644 (file)
@@ -243,6 +243,7 @@ pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> b
     eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind)
 }
 
+#[allow(clippy::too_many_lines)] // Just a big match statement
 pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
     use ItemKind::*;
     match (l, r) {
@@ -250,8 +251,20 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
         (Use(l), Use(r)) => eq_use_tree(l, r),
         (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
         (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
-        (Fn(box ast::Fn { defaultness: ld, sig: lf, generics: lg, body: lb }),
-         Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, body: rb })) => {
+        (
+            Fn(box ast::Fn {
+                defaultness: ld,
+                sig: lf,
+                generics: lg,
+                body: lb,
+            }),
+            Fn(box ast::Fn {
+                defaultness: rd,
+                sig: rf,
+                generics: rg,
+                body: rb,
+            }),
+        ) => {
             eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
         },
         (Mod(lu, lmk), Mod(ru, rmk)) => {
@@ -267,8 +280,20 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
         (ForeignMod(l), ForeignMod(r)) => {
             both(&l.abi, &r.abi, eq_str_lit) && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
         },
-        (TyAlias(box ast::TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt }),
-         TyAlias(box ast::TyAlias { defaultness: rd, generics: rg, bounds: rb, ty: rt })) => {
+        (
+            TyAlias(box ast::TyAlias {
+                defaultness: ld,
+                generics: lg,
+                bounds: lb,
+                ty: lt,
+            }),
+            TyAlias(box ast::TyAlias {
+                defaultness: rd,
+                generics: rg,
+                bounds: rb,
+                ty: rt,
+            }),
+        ) => {
             eq_defaultness(*ld, *rd)
                 && eq_generics(lg, rg)
                 && over(lb, rb, eq_generic_bound)
@@ -278,8 +303,22 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
         (Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => {
             eq_variant_data(lv, rv) && eq_generics(lg, rg)
         },
-        (Trait(box ast::Trait { is_auto: la, unsafety: lu, generics: lg, bounds: lb, items: li }),
-         Trait(box ast::Trait { is_auto: ra, unsafety: ru, generics: rg, bounds: rb, items: ri })) => {
+        (
+            Trait(box ast::Trait {
+                is_auto: la,
+                unsafety: lu,
+                generics: lg,
+                bounds: lb,
+                items: li,
+            }),
+            Trait(box ast::Trait {
+                is_auto: ra,
+                unsafety: ru,
+                generics: rg,
+                bounds: rb,
+                items: ri,
+            }),
+        ) => {
             la == ra
                 && matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
                 && eq_generics(lg, rg)
@@ -328,12 +367,36 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
     use ForeignItemKind::*;
     match (l, r) {
         (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
-        (Fn(box ast::Fn { defaultness: ld, sig: lf, generics: lg, body: lb }),
-         Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, body: rb })) => {
+        (
+            Fn(box ast::Fn {
+                defaultness: ld,
+                sig: lf,
+                generics: lg,
+                body: lb,
+            }),
+            Fn(box ast::Fn {
+                defaultness: rd,
+                sig: rf,
+                generics: rg,
+                body: rb,
+            }),
+        ) => {
             eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
         },
-        (TyAlias(box ast::TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt }),
-         TyAlias(box ast::TyAlias { defaultness: rd, generics: rg, bounds: rb, ty: rt })) => {
+        (
+            TyAlias(box ast::TyAlias {
+                defaultness: ld,
+                generics: lg,
+                bounds: lb,
+                ty: lt,
+            }),
+            TyAlias(box ast::TyAlias {
+                defaultness: rd,
+                generics: rg,
+                bounds: rb,
+                ty: rt,
+            }),
+        ) => {
             eq_defaultness(*ld, *rd)
                 && eq_generics(lg, rg)
                 && over(lb, rb, eq_generic_bound)
@@ -348,12 +411,36 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
     use AssocItemKind::*;
     match (l, r) {
         (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
-        (Fn(box ast::Fn { defaultness: ld, sig: lf, generics: lg, body: lb }),
-         Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, body: rb })) => {
+        (
+            Fn(box ast::Fn {
+                defaultness: ld,
+                sig: lf,
+                generics: lg,
+                body: lb,
+            }),
+            Fn(box ast::Fn {
+                defaultness: rd,
+                sig: rf,
+                generics: rg,
+                body: rb,
+            }),
+        ) => {
             eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
         },
-        (TyAlias(box ast::TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt }),
-         TyAlias(box ast::TyAlias { defaultness: rd, generics: rg, bounds: rb, ty: rt })) => {
+        (
+            TyAlias(box ast::TyAlias {
+                defaultness: ld,
+                generics: lg,
+                bounds: lb,
+                ty: lt,
+            }),
+            TyAlias(box ast::TyAlias {
+                defaultness: rd,
+                generics: rg,
+                bounds: rb,
+                ty: rt,
+            }),
+        ) => {
             eq_defaultness(*ld, *rd)
                 && eq_generics(lg, rg)
                 && over(lb, rb, eq_generic_bound)
index c19b558cd8c6e044902c2713e8f169fd2113920d..7ae9615d560b662095a84fd9e64c6f2070adbee7 100644 (file)
@@ -14,15 +14,14 @@ pub enum DeprecationStatus {
     None,
 }
 
+#[rustfmt::skip]
 pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
-    ("author", DeprecationStatus::None),
-    ("cognitive_complexity", DeprecationStatus::None),
-    (
-        "cyclomatic_complexity",
-        DeprecationStatus::Replaced("cognitive_complexity"),
-    ),
-    ("dump", DeprecationStatus::None),
-    ("msrv", DeprecationStatus::None),
+    ("author",                DeprecationStatus::None),
+    ("version",               DeprecationStatus::None),
+    ("cognitive_complexity",  DeprecationStatus::None),
+    ("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")),
+    ("dump",                  DeprecationStatus::None),
+    ("msrv",                  DeprecationStatus::None),
 ];
 
 pub struct LimitStack {
index 1ea7ccfb75212bb5e9a12b3c04fe514dde7343db..c2645ac730a44322423b24e07a80930c695d50a4 100644 (file)
 //!  - or-fun-call
 //!  - option-if-let-else
 
-use crate::is_ctor_or_promotable_const_function;
-use crate::ty::is_type_diagnostic_item;
+use crate::ty::{all_predicates_of, is_copy};
+use crate::visitors::is_const_evaluatable;
 use rustc_hir::def::{DefKind, Res};
-
-use rustc_hir::intravisit;
-use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
-
-use rustc_hir::{Block, Expr, ExprKind, Path, QPath};
+use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
+use rustc_hir::{def_id::DefId, Block, Expr, ExprKind, QPath, UnOp};
 use rustc_lint::LateContext;
-use rustc_middle::hir::map::Map;
-use rustc_span::sym;
-
-/// Is the expr pure (is it free from side-effects)?
-/// This function is named so to stress that it isn't exhaustive and returns FNs.
-fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
-    match expr.kind {
-        ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Path(..) | ExprKind::Field(..) => true,
-        ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr),
-        ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(identify_some_pure_patterns),
-        ExprKind::Struct(_, fields, expr) => {
-            fields.iter().all(|f| identify_some_pure_patterns(f.expr)) && expr.map_or(true, identify_some_pure_patterns)
-        },
-        ExprKind::Call(
-            &Expr {
-                kind:
-                    ExprKind::Path(QPath::Resolved(
-                        _,
-                        Path {
-                            res: Res::Def(DefKind::Ctor(..) | DefKind::Variant, ..),
-                            ..
-                        },
-                    )),
-                ..
-            },
-            args,
-        ) => args.iter().all(identify_some_pure_patterns),
-        ExprKind::Block(
-            &Block {
-                stmts,
-                expr: Some(expr),
-                ..
-            },
-            _,
-        ) => stmts.is_empty() && identify_some_pure_patterns(expr),
-        ExprKind::Box(..)
-        | ExprKind::Array(..)
-        | ExprKind::Call(..)
-        | ExprKind::MethodCall(..)
-        | ExprKind::Binary(..)
-        | ExprKind::Unary(..)
-        | ExprKind::Let(..)
-        | ExprKind::Cast(..)
-        | ExprKind::Type(..)
-        | ExprKind::DropTemps(..)
-        | ExprKind::Loop(..)
-        | ExprKind::If(..)
-        | ExprKind::Match(..)
-        | ExprKind::Closure(..)
-        | ExprKind::Block(..)
-        | ExprKind::Assign(..)
-        | ExprKind::AssignOp(..)
-        | ExprKind::Index(..)
-        | ExprKind::Break(..)
-        | ExprKind::Continue(..)
-        | ExprKind::Ret(..)
-        | ExprKind::InlineAsm(..)
-        | ExprKind::LlvmInlineAsm(..)
-        | ExprKind::Repeat(..)
-        | ExprKind::Yield(..)
-        | ExprKind::Err => false,
+use rustc_middle::ty::{self, PredicateKind};
+use rustc_span::{sym, Symbol};
+use std::cmp;
+use std::ops;
+
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+enum EagernessSuggestion {
+    // The expression is cheap and should be evaluated eagerly
+    Eager,
+    // The expression may be cheap, so don't suggested lazy evaluation; or the expression may not be safe to switch to
+    // eager evaluation.
+    NoChange,
+    // The expression is likely expensive and should be evaluated lazily.
+    Lazy,
+    // The expression cannot be placed into a closure.
+    ForceNoChange,
+}
+impl ops::BitOr for EagernessSuggestion {
+    type Output = Self;
+    fn bitor(self, rhs: Self) -> Self {
+        cmp::max(self, rhs)
     }
 }
-
-/// Identify some potentially computationally expensive patterns.
-/// This function is named so to stress that its implementation is non-exhaustive.
-/// It returns FNs and FPs.
-fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    // Searches an expression for method calls or function calls that aren't ctors
-    struct FunCallFinder<'a, 'tcx> {
-        cx: &'a LateContext<'tcx>,
-        found: bool,
+impl ops::BitOrAssign for EagernessSuggestion {
+    fn bitor_assign(&mut self, rhs: Self) {
+        *self = *self | rhs;
     }
+}
 
-    impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
-        type Map = Map<'tcx>;
-
-        fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
-            let call_found = match &expr.kind {
-                // ignore enum and struct constructors
-                ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
-                ExprKind::Index(obj, _) => {
-                    let ty = self.cx.typeck_results().expr_ty(obj);
-                    is_type_diagnostic_item(self.cx, ty, sym::HashMap)
-                        || is_type_diagnostic_item(self.cx, ty, sym::BTreeMap)
-                },
-                ExprKind::MethodCall(..) => true,
-                _ => false,
-            };
+/// Determine the eagerness of the given function call.
+fn fn_eagerness(cx: &LateContext<'tcx>, fn_id: DefId, name: Symbol, args: &'tcx [Expr<'_>]) -> EagernessSuggestion {
+    use EagernessSuggestion::{Eager, Lazy, NoChange};
+    let name = &*name.as_str();
 
-            if call_found {
-                self.found |= true;
-            }
+    let ty = match cx.tcx.impl_of_method(fn_id) {
+        Some(id) => cx.tcx.type_of(id),
+        None => return Lazy,
+    };
 
-            if !self.found {
-                intravisit::walk_expr(self, expr);
+    if (name.starts_with("as_") || name == "len" || name == "is_empty") && args.len() == 1 {
+        if matches!(
+            cx.tcx.crate_name(fn_id.krate),
+            sym::std | sym::core | sym::alloc | sym::proc_macro
+        ) {
+            Eager
+        } else {
+            NoChange
+        }
+    } else if let ty::Adt(def, subs) = ty.kind() {
+        // Types where the only fields are generic types (or references to) with no trait bounds other
+        // than marker traits.
+        // Due to the limited operations on these types functions should be fairly cheap.
+        if def
+            .variants
+            .iter()
+            .flat_map(|v| v.fields.iter())
+            .any(|x| matches!(cx.tcx.type_of(x.did).peel_refs().kind(), ty::Param(_)))
+            && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() {
+                PredicateKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker,
+                _ => true,
+            })
+            && subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_)))
+        {
+            // Limit the function to either `(self) -> bool` or `(&self) -> bool`
+            match &**cx.tcx.fn_sig(fn_id).skip_binder().inputs_and_output {
+                [arg, res] if !arg.is_mutable_ptr() && arg.peel_refs() == ty && res.is_bool() => NoChange,
+                _ => Lazy,
             }
+        } else {
+            Lazy
         }
+    } else {
+        Lazy
+    }
+}
 
+#[allow(clippy::too_many_lines)]
+fn expr_eagerness(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
+    struct V<'cx, 'tcx> {
+        cx: &'cx LateContext<'tcx>,
+        eagerness: EagernessSuggestion,
+    }
+
+    impl<'cx, 'tcx> Visitor<'tcx> for V<'cx, 'tcx> {
+        type Map = ErasedMap<'tcx>;
         fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
             NestedVisitorMap::None
         }
+
+        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+            use EagernessSuggestion::{ForceNoChange, Lazy, NoChange};
+            if self.eagerness == ForceNoChange {
+                return;
+            }
+            match e.kind {
+                ExprKind::Call(
+                    &Expr {
+                        kind: ExprKind::Path(ref path),
+                        hir_id,
+                        ..
+                    },
+                    args,
+                ) => match self.cx.qpath_res(path, hir_id) {
+                    Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => (),
+                    Res::Def(_, id) if self.cx.tcx.is_promotable_const_fn(id) => (),
+                    // No need to walk the arguments here, `is_const_evaluatable` already did
+                    Res::Def(..) if is_const_evaluatable(self.cx, e) => {
+                        self.eagerness |= NoChange;
+                        return;
+                    },
+                    Res::Def(_, id) => match path {
+                        QPath::Resolved(_, p) => {
+                            self.eagerness |= fn_eagerness(self.cx, id, p.segments.last().unwrap().ident.name, args);
+                        },
+                        QPath::TypeRelative(_, name) => {
+                            self.eagerness |= fn_eagerness(self.cx, id, name.ident.name, args);
+                        },
+                        QPath::LangItem(..) => self.eagerness = Lazy,
+                    },
+                    _ => self.eagerness = Lazy,
+                },
+                // No need to walk the arguments here, `is_const_evaluatable` already did
+                ExprKind::MethodCall(..) if is_const_evaluatable(self.cx, e) => {
+                    self.eagerness |= NoChange;
+                    return;
+                },
+                ExprKind::MethodCall(name, _, args, _) => {
+                    self.eagerness |= self
+                        .cx
+                        .typeck_results()
+                        .type_dependent_def_id(e.hir_id)
+                        .map_or(Lazy, |id| fn_eagerness(self.cx, id, name.ident.name, args));
+                },
+                ExprKind::Index(_, e) => {
+                    let ty = self.cx.typeck_results().expr_ty_adjusted(e);
+                    if is_copy(self.cx, ty) && !ty.is_ref() {
+                        self.eagerness |= NoChange;
+                    } else {
+                        self.eagerness = Lazy;
+                    }
+                },
+
+                // Dereferences should be cheap, but dereferencing a raw pointer earlier may not be safe.
+                ExprKind::Unary(UnOp::Deref, e) if !self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => (),
+                ExprKind::Unary(UnOp::Deref, _) => self.eagerness |= NoChange,
+
+                ExprKind::Unary(_, e)
+                    if matches!(
+                        self.cx.typeck_results().expr_ty(e).kind(),
+                        ty::Bool | ty::Int(_) | ty::Uint(_),
+                    ) => {},
+                ExprKind::Binary(_, lhs, rhs)
+                    if self.cx.typeck_results().expr_ty(lhs).is_primitive()
+                        && self.cx.typeck_results().expr_ty(rhs).is_primitive() => {},
+
+                // Can't be moved into a closure
+                ExprKind::Break(..)
+                | ExprKind::Continue(_)
+                | ExprKind::Ret(_)
+                | ExprKind::InlineAsm(_)
+                | ExprKind::LlvmInlineAsm(_)
+                | ExprKind::Yield(..)
+                | ExprKind::Err => {
+                    self.eagerness = ForceNoChange;
+                    return;
+                },
+
+                // Memory allocation, custom operator, loop, or call to an unknown function
+                ExprKind::Box(_)
+                | ExprKind::Unary(..)
+                | ExprKind::Binary(..)
+                | ExprKind::Loop(..)
+                | ExprKind::Call(..) => self.eagerness = Lazy,
+
+                ExprKind::ConstBlock(_)
+                | ExprKind::Array(_)
+                | ExprKind::Tup(_)
+                | ExprKind::Lit(_)
+                | ExprKind::Cast(..)
+                | ExprKind::Type(..)
+                | ExprKind::DropTemps(_)
+                | ExprKind::Let(..)
+                | ExprKind::If(..)
+                | ExprKind::Match(..)
+                | ExprKind::Closure(..)
+                | ExprKind::Field(..)
+                | ExprKind::Path(_)
+                | ExprKind::AddrOf(..)
+                | ExprKind::Struct(..)
+                | ExprKind::Repeat(..)
+                | ExprKind::Block(Block { stmts: [], .. }, _) => (),
+
+                // Assignment might be to a local defined earlier, so don't eagerly evaluate.
+                // Blocks with multiple statements might be expensive, so don't eagerly evaluate.
+                // TODO: Actually check if either of these are true here.
+                ExprKind::Assign(..) | ExprKind::AssignOp(..) | ExprKind::Block(..) => self.eagerness |= NoChange,
+            }
+            walk_expr(self, e);
+        }
     }
 
-    let mut finder = FunCallFinder { cx, found: false };
-    finder.visit_expr(expr);
-    finder.found
+    let mut v = V {
+        cx,
+        eagerness: EagernessSuggestion::Eager,
+    };
+    v.visit_expr(e);
+    v.eagerness
 }
 
-pub fn is_eagerness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    !identify_some_potentially_expensive_patterns(cx, expr) && identify_some_pure_patterns(expr)
+/// Whether the given expression should be changed to evaluate eagerly
+pub fn switch_to_eager_eval(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+    expr_eagerness(cx, expr) == EagernessSuggestion::Eager
 }
 
-pub fn is_lazyness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    identify_some_potentially_expensive_patterns(cx, expr)
+/// Whether the given expression should be changed to evaluate lazily
+pub fn switch_to_lazy_eval(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+    expr_eagerness(cx, expr) == EagernessSuggestion::Lazy
 }
index 086fbc9d3ddddcfba357846e45e897f806118814..f011380c127a25c10939ed22c1cb07796248e5a4 100644 (file)
@@ -1,6 +1,7 @@
 #![feature(box_patterns)]
 #![feature(in_band_lifetimes)]
 #![feature(iter_zip)]
+#![feature(let_else)]
 #![feature(rustc_private)]
 #![feature(control_flow_enum)]
 #![recursion_limit = "512"]
@@ -68,7 +69,7 @@
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::hir_id::{HirIdMap, HirIdSet};
-use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
 use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 use rustc_hir::{
@@ -96,6 +97,7 @@
 
 use crate::consts::{constant, Constant};
 use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type};
+use crate::visitors::expr_visitor_no_bodies;
 
 pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
     if let Ok(version) = RustcVersion::parse(msrv) {
@@ -250,12 +252,6 @@ pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem
     false
 }
 
-/// Returns `true` if this `span` was expanded by any macro.
-#[must_use]
-pub fn in_macro(span: Span) -> bool {
-    span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
-}
-
 pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
     matches!(
         expr.kind,
@@ -1113,63 +1109,30 @@ pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
 
 /// Returns `true` if `expr` contains a return expression
 pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
-    struct RetCallFinder {
-        found: bool,
-    }
-
-    impl<'tcx> hir::intravisit::Visitor<'tcx> for RetCallFinder {
-        type Map = Map<'tcx>;
-
-        fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
-            if self.found {
-                return;
-            }
+    let mut found = false;
+    expr_visitor_no_bodies(|expr| {
+        if !found {
             if let hir::ExprKind::Ret(..) = &expr.kind {
-                self.found = true;
-            } else {
-                hir::intravisit::walk_expr(self, expr);
+                found = true;
             }
         }
-
-        fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
-            hir::intravisit::NestedVisitorMap::None
-        }
-    }
-
-    let mut visitor = RetCallFinder { found: false };
-    visitor.visit_expr(expr);
-    visitor.found
-}
-
-struct FindMacroCalls<'a, 'b> {
-    names: &'a [&'b str],
-    result: Vec<Span>,
-}
-
-impl<'a, 'b, 'tcx> Visitor<'tcx> for FindMacroCalls<'a, 'b> {
-    type Map = Map<'tcx>;
-
-    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-        if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
-            self.result.push(expr.span);
-        }
-        // and check sub-expressions
-        intravisit::walk_expr(self, expr);
-    }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
+        !found
+    })
+    .visit_expr(expr);
+    found
 }
 
 /// Finds calls of the specified macros in a function body.
 pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
-    let mut fmc = FindMacroCalls {
-        names,
-        result: Vec::new(),
-    };
-    fmc.visit_expr(&body.value);
-    fmc.result
+    let mut result = Vec::new();
+    expr_visitor_no_bodies(|expr| {
+        if names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
+            result.push(expr.span);
+        }
+        true
+    })
+    .visit_expr(&body.value);
+    result
 }
 
 /// Extends the span to the beginning of the spans line, incl. whitespaces.
@@ -1439,7 +1402,7 @@ pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>
 /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
 /// implementations have.
 pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
-    attrs.iter().any(|attr| attr.has_name(sym::automatically_derived))
+    has_attr(attrs, sym::automatically_derived)
 }
 
 /// Remove blocks around an expression.
@@ -1561,20 +1524,29 @@ pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
     (u << amt) >> amt
 }
 
-pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
+pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool {
+    attrs.iter().any(|attr| attr.has_name(symbol))
+}
+
+pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
     let map = &tcx.hir();
     let mut prev_enclosing_node = None;
     let mut enclosing_node = node;
     while Some(enclosing_node) != prev_enclosing_node {
-        if is_automatically_derived(map.attrs(enclosing_node)) {
+        if has_attr(map.attrs(enclosing_node), symbol) {
             return true;
         }
         prev_enclosing_node = Some(enclosing_node);
         enclosing_node = map.get_parent_item(enclosing_node);
     }
+
     false
 }
 
+pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
+    any_parent_has_attr(tcx, node, sym::automatically_derived)
+}
+
 /// Matches a function call with the given path and returns the arguments.
 ///
 /// Usage:
@@ -1625,6 +1597,14 @@ pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -
     syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
 }
 
+/// Checks if the given `DefId` matches the `libc` item.
+pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
+    let path = cx.get_def_path(did);
+    // libc is meant to be used as a flat list of names, but they're all actually defined in different
+    // modules based on the target platform. Ignore everything but crate name and the item name.
+    path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
+}
+
 pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
     if let ExprKind::Call(func, [arg]) = expr.kind {
         expr_path_res(cx, func)
@@ -1837,6 +1817,16 @@ pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
     matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
 }
 
+pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
+    if !is_no_std_crate(cx) {
+        Some("std")
+    } else if !is_no_core_crate(cx) {
+        Some("core")
+    } else {
+        None
+    }
+}
+
 pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
     cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
         if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
@@ -1847,6 +1837,16 @@ pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
     })
 }
 
+pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
+    cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
+        if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
+            attr.path == sym::no_core
+        } else {
+            false
+        }
+    })
+}
+
 /// Check if parent of a hir node is a trait implementation block.
 /// For example, `f` in
 /// ```rust,ignore
index fa57dfbb57edccdc4d384af5ac4a0e226ab9ea2f..0cec7d6a5e402aa13a05395c23bf5dd630a5d587 100644 (file)
@@ -19,7 +19,7 @@ macro_rules! msrv_aliases {
     1,46,0 { CONST_IF_MATCH }
     1,45,0 { STR_STRIP_PREFIX }
     1,43,0 { LOG2_10, LOG10_2 }
-    1,42,0 { MATCHES_MACRO }
+    1,42,0 { MATCHES_MACRO, SLICE_PATTERNS }
     1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
     1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
     1,38,0 { POINTER_CAST }
@@ -27,7 +27,8 @@ macro_rules! msrv_aliases {
     1,36,0 { ITERATOR_COPIED }
     1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
     1,34,0 { TRY_FROM }
-    1,30,0 { ITERATOR_FIND_MAP }
+    1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
+    1,28,0 { FROM_BOOL }
     1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST }
     1,16,0 { STR_REPEAT }
 }
index 501b08a47f161103b9f34b46760ae2502df8d4bc..6171823abbbd04fa7142aecaad1231c654150ff2 100644 (file)
@@ -28,6 +28,7 @@
 pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
 /// Preferably use the diagnostic item `sym::Borrow` where possible
 pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
+pub const BORROW_MUT_TRAIT: [&str; 3] = ["core", "borrow", "BorrowMut"];
 pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
 pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
 pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
@@ -85,7 +86,6 @@
 pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 #[cfg(feature = "internal-lints")]
 pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
-pub const LIBC_STRLEN: [&str; 2] = ["libc", "strlen"];
 #[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
 pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
 pub(super) const PANICKING_PANIC_FMT: [&str; 3] = ["core", "panicking", "panic_fmt"];
 pub(super) const PANICKING_PANIC_STR: [&str; 3] = ["core", "panicking", "panic_str"];
 pub(super) const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
+pub const PARKING_LOT_RAWMUTEX: [&str; 3] = ["parking_lot", "raw_mutex", "RawMutex"];
+pub const PARKING_LOT_RAWRWLOCK: [&str; 3] = ["parking_lot", "raw_rwlock", "RawRwLock"];
 pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"];
 pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"];
 pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"];
 pub const WRITE_MACRO: [&str; 3] = ["core", "macros", "write"];
 #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
 pub const WRITELN_MACRO: [&str; 3] = ["core", "macros", "writeln"];
+pub const PTR_NON_NULL: [&str; 4] = ["core", "ptr", "non_null", "NonNull"];
index 8adb691595213fd80cd036f1aa50fa20cadd97a1..17d9a505bc9df7a601c81cd8639b3bcb701ae2ac 100644 (file)
@@ -1,9 +1,9 @@
 use crate::source::snippet;
+use crate::visitors::expr_visitor_no_bodies;
 use crate::{path_to_local_id, strip_pat_refs};
-use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{Body, BodyId, Expr, ExprKind, HirId, PatKind};
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind};
 use rustc_lint::LateContext;
-use rustc_middle::hir::map::Map;
 use rustc_span::Span;
 use std::borrow::Cow;
 
@@ -30,50 +30,28 @@ fn extract_clone_suggestions<'tcx>(
     replace: &[(&'static str, &'static str)],
     body: &'tcx Body<'_>,
 ) -> Option<Vec<(Span, Cow<'static, str>)>> {
-    let mut visitor = PtrCloneVisitor {
-        cx,
-        id,
-        replace,
-        spans: vec![],
-        abort: false,
-    };
-    visitor.visit_body(body);
-    if visitor.abort { None } else { Some(visitor.spans) }
-}
-
-struct PtrCloneVisitor<'a, 'tcx> {
-    cx: &'a LateContext<'tcx>,
-    id: HirId,
-    replace: &'a [(&'static str, &'static str)],
-    spans: Vec<(Span, Cow<'static, str>)>,
-    abort: bool,
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
-    type Map = Map<'tcx>;
-
-    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-        if self.abort {
-            return;
+    let mut abort = false;
+    let mut spans = Vec::new();
+    expr_visitor_no_bodies(|expr| {
+        if abort {
+            return false;
         }
         if let ExprKind::MethodCall(seg, _, [recv], _) = expr.kind {
-            if path_to_local_id(recv, self.id) {
+            if path_to_local_id(recv, id) {
                 if seg.ident.name.as_str() == "capacity" {
-                    self.abort = true;
-                    return;
+                    abort = true;
+                    return false;
                 }
-                for &(fn_name, suffix) in self.replace {
+                for &(fn_name, suffix) in replace {
                     if seg.ident.name.as_str() == fn_name {
-                        self.spans.push((expr.span, snippet(self.cx, recv.span, "_") + suffix));
-                        return;
+                        spans.push((expr.span, snippet(cx, recv.span, "_") + suffix));
+                        return false;
                     }
                 }
             }
         }
-        walk_expr(self, expr);
-    }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
+        !abort
+    })
+    .visit_body(body);
+    if abort { None } else { Some(spans) }
 }
index 789079510c5e80fa7251536eeda79336d99e7777..d928317259da0f23b875300b02f3fe3c650b20c1 100644 (file)
@@ -128,7 +128,7 @@ pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<us
 fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
     let x = s
         .lines()
-        .skip(ignore_first as usize)
+        .skip(usize::from(ignore_first))
         .filter_map(|l| {
             if l.is_empty() {
                 None
@@ -155,14 +155,22 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>,
         .join("\n")
 }
 
-/// Converts a span to a code snippet if available, otherwise use default.
+/// Converts a span to a code snippet if available, otherwise returns the default.
 ///
 /// This is useful if you want to provide suggestions for your lint or more generally, if you want
-/// to convert a given `Span` to a `str`.
+/// to convert a given `Span` to a `str`. To create suggestions consider using
+/// [`snippet_with_applicability`] to ensure that the applicability stays correct.
 ///
 /// # Example
 /// ```rust,ignore
-/// snippet(cx, expr.span, "..")
+/// // Given two spans one for `value` and one for the `init` expression.
+/// let value = Vec::new();
+/// //  ^^^^^   ^^^^^^^^^^
+/// //  span1   span2
+///
+/// // The snipped call would return the corresponding code snippet
+/// snippet(cx, span1, "..") // -> "value"
+/// snippet(cx, span2, "..") // -> "Vec::new()"
 /// ```
 pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
     snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
index 01fb944cc36f64dcfd266726ce70bf4aa6ba5418..872942685f0d126cb01dfb6e492aab6b7d5a74e0 100644 (file)
@@ -1,19 +1,27 @@
 //! Contains utility functions to generate suggestions.
 #![deny(clippy::missing_docs_in_private_items)]
 
-use crate::higher;
-use crate::source::{snippet, snippet_opt, snippet_with_context, snippet_with_macro_callsite};
+use crate::source::{
+    snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite,
+};
+use crate::{get_parent_expr_for_hir, higher};
 use rustc_ast::util::parser::AssocOp;
 use rustc_ast::{ast, token};
 use rustc_ast_pretty::pprust::token_kind_to_string;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
+use rustc_hir::{ExprKind, HirId, MutTy, TyKind};
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{EarlyContext, LateContext, LintContext};
-use rustc_span::source_map::{CharPos, Span};
-use rustc_span::{BytePos, Pos, SyntaxContext};
+use rustc_middle::hir::place::ProjectionKind;
+use rustc_middle::mir::{FakeReadCause, Mutability};
+use rustc_middle::ty;
+use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext};
+use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 use std::borrow::Cow;
 use std::convert::TryInto;
 use std::fmt::Display;
+use std::iter;
 use std::ops::{Add, Neg, Not, Sub};
 
 /// A helper type to build suggestion correctly handling parentheses.
@@ -716,6 +724,267 @@ fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability:
     }
 }
 
+/// Suggestion results for handling closure
+/// args dereferencing and borrowing
+pub struct DerefClosure {
+    /// confidence on the built suggestion
+    pub applicability: Applicability,
+    /// gradually built suggestion
+    pub suggestion: String,
+}
+
+/// Build suggestion gradually by handling closure arg specific usages,
+/// such as explicit deref and borrowing cases.
+/// Returns `None` if no such use cases have been triggered in closure body
+///
+/// note: this only works on single line immutable closures with exactly one input parameter.
+pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<'_>) -> Option<DerefClosure> {
+    if let hir::ExprKind::Closure(_, fn_decl, body_id, ..) = closure.kind {
+        let closure_body = cx.tcx.hir().body(body_id);
+        // is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`)
+        // a type annotation is present if param `kind` is different from `TyKind::Infer`
+        let closure_arg_is_type_annotated_double_ref = if let TyKind::Rptr(_, MutTy { ty, .. }) = fn_decl.inputs[0].kind
+        {
+            matches!(ty.kind, TyKind::Rptr(_, MutTy { .. }))
+        } else {
+            false
+        };
+
+        let mut visitor = DerefDelegate {
+            cx,
+            closure_span: closure.span,
+            closure_arg_is_type_annotated_double_ref,
+            next_pos: closure.span.lo(),
+            suggestion_start: String::new(),
+            applicability: Applicability::MaybeIncorrect,
+        };
+
+        let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id);
+        cx.tcx.infer_ctxt().enter(|infcx| {
+            ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
+                .consume_body(closure_body);
+        });
+
+        if !visitor.suggestion_start.is_empty() {
+            return Some(DerefClosure {
+                applicability: visitor.applicability,
+                suggestion: visitor.finish(),
+            });
+        }
+    }
+    None
+}
+
+/// Visitor struct used for tracking down
+/// dereferencing and borrowing of closure's args
+struct DerefDelegate<'a, 'tcx> {
+    /// The late context of the lint
+    cx: &'a LateContext<'tcx>,
+    /// The span of the input closure to adapt
+    closure_span: Span,
+    /// Indicates if the arg of the closure is a type annotated double reference
+    closure_arg_is_type_annotated_double_ref: bool,
+    /// last position of the span to gradually build the suggestion
+    next_pos: BytePos,
+    /// starting part of the gradually built suggestion
+    suggestion_start: String,
+    /// confidence on the built suggestion
+    applicability: Applicability,
+}
+
+impl DerefDelegate<'_, 'tcx> {
+    /// build final suggestion:
+    /// - create the ending part of suggestion
+    /// - concatenate starting and ending parts
+    /// - potentially remove needless borrowing
+    pub fn finish(&mut self) -> String {
+        let end_span = Span::new(self.next_pos, self.closure_span.hi(), self.closure_span.ctxt(), None);
+        let end_snip = snippet_with_applicability(self.cx, end_span, "..", &mut self.applicability);
+        let sugg = format!("{}{}", self.suggestion_start, end_snip);
+        if self.closure_arg_is_type_annotated_double_ref {
+            sugg.replacen('&', "", 1)
+        } else {
+            sugg
+        }
+    }
+
+    /// indicates whether the function from `parent_expr` takes its args by double reference
+    fn func_takes_arg_by_double_ref(&self, parent_expr: &'tcx hir::Expr<'_>, cmt_hir_id: HirId) -> bool {
+        let (call_args, inputs) = match parent_expr.kind {
+            ExprKind::MethodCall(_, _, call_args, _) => {
+                if let Some(method_did) = self.cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) {
+                    (call_args, self.cx.tcx.fn_sig(method_did).skip_binder().inputs())
+                } else {
+                    return false;
+                }
+            },
+            ExprKind::Call(func, call_args) => {
+                let typ = self.cx.typeck_results().expr_ty(func);
+                (call_args, typ.fn_sig(self.cx.tcx).skip_binder().inputs())
+            },
+            _ => return false,
+        };
+
+        iter::zip(call_args, inputs)
+            .any(|(arg, ty)| arg.hir_id == cmt_hir_id && matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
+    }
+}
+
+impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
+    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
+
+    #[allow(clippy::too_many_lines)]
+    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
+        if let PlaceBase::Local(id) = cmt.place.base {
+            let map = self.cx.tcx.hir();
+            let span = map.span(cmt.hir_id);
+            let start_span = Span::new(self.next_pos, span.lo(), span.ctxt(), None);
+            let mut start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability);
+
+            // identifier referring to the variable currently triggered (i.e.: `fp`)
+            let ident_str = map.name(id).to_string();
+            // full identifier that includes projection (i.e.: `fp.field`)
+            let ident_str_with_proj = snippet(self.cx, span, "..").to_string();
+
+            if cmt.place.projections.is_empty() {
+                // handle item without any projection, that needs an explicit borrowing
+                // i.e.: suggest `&x` instead of `x`
+                self.suggestion_start.push_str(&format!("{}&{}", start_snip, ident_str));
+            } else {
+                // cases where a parent `Call` or `MethodCall` is using the item
+                // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()`
+                //
+                // Note about method calls:
+                // - compiler automatically dereference references if the target type is a reference (works also for
+                //   function call)
+                // - `self` arguments in the case of `x.is_something()` are also automatically (de)referenced, and
+                //   no projection should be suggested
+                if let Some(parent_expr) = get_parent_expr_for_hir(self.cx, cmt.hir_id) {
+                    match &parent_expr.kind {
+                        // given expression is the self argument and will be handled completely by the compiler
+                        // i.e.: `|x| x.is_something()`
+                        ExprKind::MethodCall(_, _, [self_expr, ..], _) if self_expr.hir_id == cmt.hir_id => {
+                            self.suggestion_start
+                                .push_str(&format!("{}{}", start_snip, ident_str_with_proj));
+                            self.next_pos = span.hi();
+                            return;
+                        },
+                        // item is used in a call
+                        // i.e.: `Call`: `|x| please(x)` or `MethodCall`: `|x| [1, 2, 3].contains(x)`
+                        ExprKind::Call(_, [call_args @ ..]) | ExprKind::MethodCall(_, _, [_, call_args @ ..], _) => {
+                            let expr = self.cx.tcx.hir().expect_expr(cmt.hir_id);
+                            let arg_ty_kind = self.cx.typeck_results().expr_ty(expr).kind();
+
+                            if matches!(arg_ty_kind, ty::Ref(_, _, Mutability::Not)) {
+                                // suggest ampersand if call function is taking args by double reference
+                                let takes_arg_by_double_ref =
+                                    self.func_takes_arg_by_double_ref(parent_expr, cmt.hir_id);
+
+                                // compiler will automatically dereference field or index projection, so no need
+                                // to suggest ampersand, but full identifier that includes projection is required
+                                let has_field_or_index_projection =
+                                    cmt.place.projections.iter().any(|proj| {
+                                        matches!(proj.kind, ProjectionKind::Field(..) | ProjectionKind::Index)
+                                    });
+
+                                // no need to bind again if the function doesn't take arg by double ref
+                                // and if the item is already a double ref
+                                let ident_sugg = if !call_args.is_empty()
+                                    && !takes_arg_by_double_ref
+                                    && (self.closure_arg_is_type_annotated_double_ref || has_field_or_index_projection)
+                                {
+                                    let ident = if has_field_or_index_projection {
+                                        ident_str_with_proj
+                                    } else {
+                                        ident_str
+                                    };
+                                    format!("{}{}", start_snip, ident)
+                                } else {
+                                    format!("{}&{}", start_snip, ident_str)
+                                };
+                                self.suggestion_start.push_str(&ident_sugg);
+                                self.next_pos = span.hi();
+                                return;
+                            }
+
+                            self.applicability = Applicability::Unspecified;
+                        },
+                        _ => (),
+                    }
+                }
+
+                let mut replacement_str = ident_str;
+                let mut projections_handled = false;
+                cmt.place.projections.iter().enumerate().for_each(|(i, proj)| {
+                    match proj.kind {
+                        // Field projection like `|v| v.foo`
+                        // no adjustment needed here, as field projections are handled by the compiler
+                        ProjectionKind::Field(..) => match cmt.place.ty_before_projection(i).kind() {
+                            ty::Adt(..) | ty::Tuple(_) => {
+                                replacement_str = ident_str_with_proj.clone();
+                                projections_handled = true;
+                            },
+                            _ => (),
+                        },
+                        // Index projection like `|x| foo[x]`
+                        // the index is dropped so we can't get it to build the suggestion,
+                        // so the span is set-up again to get more code, using `span.hi()` (i.e.: `foo[x]`)
+                        // instead of `span.lo()` (i.e.: `foo`)
+                        ProjectionKind::Index => {
+                            let start_span = Span::new(self.next_pos, span.hi(), span.ctxt(), None);
+                            start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability);
+                            replacement_str.clear();
+                            projections_handled = true;
+                        },
+                        // note: unable to trigger `Subslice` kind in tests
+                        ProjectionKind::Subslice => (),
+                        ProjectionKind::Deref => {
+                            // Explicit derefs are typically handled later on, but
+                            // some items do not need explicit deref, such as array accesses,
+                            // so we mark them as already processed
+                            // i.e.: don't suggest `*sub[1..4].len()` for `|sub| sub[1..4].len() == 3`
+                            if let ty::Ref(_, inner, _) = cmt.place.ty_before_projection(i).kind() {
+                                if matches!(inner.kind(), ty::Ref(_, innermost, _) if innermost.is_array()) {
+                                    projections_handled = true;
+                                }
+                            }
+                        },
+                    }
+                });
+
+                // handle `ProjectionKind::Deref` by removing one explicit deref
+                // if no special case was detected (i.e.: suggest `*x` instead of `**x`)
+                if !projections_handled {
+                    let last_deref = cmt
+                        .place
+                        .projections
+                        .iter()
+                        .rposition(|proj| proj.kind == ProjectionKind::Deref);
+
+                    if let Some(pos) = last_deref {
+                        let mut projections = cmt.place.projections.clone();
+                        projections.truncate(pos);
+
+                        for item in projections {
+                            if item.kind == ProjectionKind::Deref {
+                                replacement_str = format!("*{}", replacement_str);
+                            }
+                        }
+                    }
+                }
+
+                self.suggestion_start
+                    .push_str(&format!("{}{}", start_snip, replacement_str));
+            }
+            self.next_pos = span.hi();
+        }
+    }
+
+    fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
+
+    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
+}
+
 #[cfg(test)]
 mod test {
     use super::Sugg;
index ca64ac7de3eea4b98fd9f609a82d153008dbc9df..438c39bea0a0ea6d4648bf81ed8ffa7b615638b8 100644 (file)
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, TypeFoldable, UintTy};
-use rustc_span::sym;
-use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::DUMMY_SP;
+use rustc_middle::ty::{self, AdtDef, IntTy, Predicate, Ty, TyCtxt, TypeFoldable, UintTy};
+use rustc_span::symbol::Ident;
+use rustc_span::{sym, Span, Symbol, DUMMY_SP};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::query::normalize::AtExt;
+use std::iter;
 
 use crate::{match_def_path, must_use_attr};
 
+// Checks if the given type implements copy.
 pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env)
 }
@@ -114,7 +115,12 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<
 
 /// Checks whether a type implements a trait.
 /// The function returns false in case the type contains an inference variable.
-/// See also [`get_trait_def_id`](super::get_trait_def_id).
+///
+/// See:
+/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
+/// * [Common tools for writing lints] for an example how to use this function and other options.
+///
+/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
 pub fn implements_trait<'tcx>(
     cx: &LateContext<'tcx>,
     ty: Ty<'tcx>,
@@ -254,9 +260,17 @@ pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_ite
     }
 }
 
-/// Checks if the type is equal to a diagnostic item
+/// Checks if the type is equal to a diagnostic item. To check if a type implements a
+/// trait marked with a diagnostic item use [`implements_trait`].
+///
+/// For a further exploitation what diagnostic items are see [diagnostic items] in
+/// rustc-dev-guide.
+///
+/// ---
 ///
 /// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
+///
+/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
 pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
     match ty.kind() {
         ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
@@ -377,3 +391,16 @@ pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
         _ => false,
     }
 }
+
+/// Gets an iterator over all predicates which apply to the given item.
+pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(Predicate<'_>, Span)> {
+    let mut next_id = Some(id);
+    iter::from_fn(move || {
+        next_id.take().map(|id| {
+            let preds = tcx.predicates_of(id);
+            next_id = preds.parent;
+            preds.predicates.iter()
+        })
+    })
+    .flatten()
+}
index 34206b5ae2b21a14291aa6c3c7c2af0f9b579d11..dfe8a66c2a1b53f9b3d48b2352460491b3906454 100644 (file)
@@ -1,7 +1,7 @@
 use crate as utils;
+use crate::visitors::{expr_visitor, expr_visitor_no_bodies};
 use rustc_hir as hir;
-use rustc_hir::intravisit;
-use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::HirIdSet;
 use rustc_hir::{Expr, ExprKind, HirId};
 use rustc_infer::infer::TyCtxtInferExt;
@@ -148,96 +148,47 @@ fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
     }
 }
 
-struct ReturnBreakContinueMacroVisitor {
-    seen_return_break_continue: bool,
-}
-
-impl ReturnBreakContinueMacroVisitor {
-    fn new() -> ReturnBreakContinueMacroVisitor {
-        ReturnBreakContinueMacroVisitor {
-            seen_return_break_continue: false,
-        }
-    }
-}
-
-impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
-    type Map = Map<'tcx>;
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
-
-    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
-        if self.seen_return_break_continue {
-            // No need to look farther if we've already seen one of them
-            return;
+pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
+    let mut seen_return_break_continue = false;
+    expr_visitor_no_bodies(|ex| {
+        if seen_return_break_continue {
+            return false;
         }
         match &ex.kind {
             ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
-                self.seen_return_break_continue = true;
+                seen_return_break_continue = true;
             },
             // Something special could be done here to handle while or for loop
             // desugaring, as this will detect a break if there's a while loop
             // or a for loop inside the expression.
             _ => {
-                if utils::in_macro(ex.span) {
-                    self.seen_return_break_continue = true;
-                } else {
-                    rustc_hir::intravisit::walk_expr(self, ex);
+                if ex.span.from_expansion() {
+                    seen_return_break_continue = true;
                 }
             },
         }
-    }
-}
-
-pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
-    let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new();
-    recursive_visitor.visit_expr(expression);
-    recursive_visitor.seen_return_break_continue
-}
-
-pub struct UsedAfterExprVisitor<'a, 'tcx> {
-    cx: &'a LateContext<'tcx>,
-    expr: &'tcx Expr<'tcx>,
-    definition: HirId,
-    past_expr: bool,
-    used_after_expr: bool,
-}
-impl<'a, 'tcx> UsedAfterExprVisitor<'a, 'tcx> {
-    pub fn is_found(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
-        utils::path_to_local(expr).map_or(false, |definition| {
-            let mut visitor = UsedAfterExprVisitor {
-                cx,
-                expr,
-                definition,
-                past_expr: false,
-                used_after_expr: false,
-            };
-            utils::get_enclosing_block(cx, definition).map_or(false, |block| {
-                visitor.visit_block(block);
-                visitor.used_after_expr
-            })
-        })
-    }
-}
-
-impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedAfterExprVisitor<'a, 'tcx> {
-    type Map = Map<'tcx>;
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
-    }
-
-    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
-        if self.used_after_expr {
-            return;
+        !seen_return_break_continue
+    })
+    .visit_expr(expression);
+    seen_return_break_continue
+}
+
+pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool {
+    let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false };
+    let mut used_after_expr = false;
+    let mut past_expr = false;
+    expr_visitor(cx, |expr| {
+        if used_after_expr {
+            return false;
         }
 
-        if expr.hir_id == self.expr.hir_id {
-            self.past_expr = true;
-        } else if self.past_expr && utils::path_to_local_id(expr, self.definition) {
-            self.used_after_expr = true;
-        } else {
-            intravisit::walk_expr(self, expr);
+        if expr.hir_id == after.hir_id {
+            past_expr = true;
+        } else if past_expr && utils::path_to_local_id(expr, local_id) {
+            used_after_expr = true;
         }
-    }
+        !used_after_expr
+    })
+    .visit_block(block);
+    used_after_expr
 }
index 503effbdad5725069eb4ddc268a08c80ae7b9014..4bfd3c64b9c361acb61001600277e3064bbb2ff3 100644 (file)
@@ -1,38 +1,70 @@
 use crate::path_to_local_id;
 use rustc_hir as hir;
-use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
-use rustc_hir::{def::Res, Arm, Block, Body, BodyId, Destination, Expr, ExprKind, HirId, Stmt};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::intravisit::{self, walk_block, walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::{
+    Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, Unsafety,
+};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
-use std::ops::ControlFlow;
+use rustc_middle::ty;
 
-/// returns `true` if expr contains match expr desugared from try
-fn contains_try(expr: &hir::Expr<'_>) -> bool {
-    struct TryFinder {
-        found: bool,
+/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
+/// bodies (i.e. closures) are visited.
+/// If the callback returns `true`, the expr just provided to the callback is walked.
+#[must_use]
+pub fn expr_visitor<'tcx>(cx: &LateContext<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
+    struct V<'tcx, F> {
+        hir: Map<'tcx>,
+        f: F,
     }
+    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<'tcx, F> {
+        type Map = Map<'tcx>;
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::OnlyBodies(self.hir)
+        }
 
-    impl<'hir> intravisit::Visitor<'hir> for TryFinder {
-        type Map = Map<'hir>;
+        fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+            if (self.f)(expr) {
+                walk_expr(self, expr);
+            }
+        }
+    }
+    V { hir: cx.tcx.hir(), f }
+}
 
-        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
-            intravisit::NestedVisitorMap::None
+/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
+/// bodies (i.e. closures) are not visited.
+/// If the callback returns `true`, the expr just provided to the callback is walked.
+#[must_use]
+pub fn expr_visitor_no_bodies<'tcx>(f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
+    struct V<F>(F);
+    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<F> {
+        type Map = intravisit::ErasedMap<'tcx>;
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::None
         }
 
-        fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
-            if self.found {
-                return;
-            }
-            match expr.kind {
-                hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true,
-                _ => intravisit::walk_expr(self, expr),
+        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+            if (self.0)(e) {
+                walk_expr(self, e);
             }
         }
     }
+    V(f)
+}
 
-    let mut visitor = TryFinder { found: false };
-    visitor.visit_expr(expr);
-    visitor.found
+/// returns `true` if expr contains match expr desugared from try
+fn contains_try(expr: &hir::Expr<'_>) -> bool {
+    let mut found = false;
+    expr_visitor_no_bodies(|e| {
+        if !found {
+            found = matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar));
+        }
+        !found
+    })
+    .visit_expr(expr);
+    found
 }
 
 pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
@@ -165,103 +197,186 @@ fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
 //     }
 // }
 
-/// Calls the given function for each break expression.
-pub fn visit_break_exprs<'tcx>(
-    node: impl Visitable<'tcx>,
-    f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>),
-) {
-    struct V<F>(F);
-    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>)> Visitor<'tcx> for V<F> {
-        type Map = ErasedMap<'tcx>;
-        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-            NestedVisitorMap::None
+/// Checks if the given resolved path is used in the given body.
+pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
+    let mut found = false;
+    expr_visitor(cx, |e| {
+        if found {
+            return false;
         }
 
-        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
-            if let ExprKind::Break(dest, sub_expr) = e.kind {
-                self.0(e, dest, sub_expr);
+        if let ExprKind::Path(p) = &e.kind {
+            if cx.qpath_res(p, e.hir_id) == res {
+                found = true;
             }
-            walk_expr(self, e);
         }
-    }
+        !found
+    })
+    .visit_expr(&cx.tcx.hir().body(body).value);
+    found
+}
 
-    node.visit(&mut V(f));
+/// Checks if the given local is used.
+pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
+    let mut is_used = false;
+    let mut visitor = expr_visitor(cx, |expr| {
+        if !is_used {
+            is_used = path_to_local_id(expr, id);
+        }
+        !is_used
+    });
+    visitable.visit(&mut visitor);
+    drop(visitor);
+    is_used
 }
 
-/// Checks if the given resolved path is used in the given body.
-pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
+/// Checks if the given expression is a constant.
+pub fn is_const_evaluatable(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
     struct V<'a, 'tcx> {
         cx: &'a LateContext<'tcx>,
-        res: Res,
-        found: bool,
+        is_const: bool,
     }
-    impl Visitor<'tcx> for V<'_, 'tcx> {
+    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
         type Map = Map<'tcx>;
         fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
             NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
         }
 
         fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
-            if self.found {
+            if !self.is_const {
                 return;
             }
+            match e.kind {
+                ExprKind::ConstBlock(_) => return,
+                ExprKind::Call(
+                    &Expr {
+                        kind: ExprKind::Path(ref p),
+                        hir_id,
+                        ..
+                    },
+                    _,
+                ) if self
+                    .cx
+                    .qpath_res(p, hir_id)
+                    .opt_def_id()
+                    .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
+                ExprKind::MethodCall(..)
+                    if self
+                        .cx
+                        .typeck_results()
+                        .type_dependent_def_id(e.hir_id)
+                        .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
+                ExprKind::Binary(_, lhs, rhs)
+                    if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
+                        && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
+                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (),
+                ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
+                ExprKind::Index(base, _)
+                    if matches!(
+                        self.cx.typeck_results().expr_ty(base).peel_refs().kind(),
+                        ty::Slice(_) | ty::Array(..)
+                    ) => {},
+                ExprKind::Path(ref p)
+                    if matches!(
+                        self.cx.qpath_res(p, e.hir_id),
+                        Res::Def(
+                            DefKind::Const
+                                | DefKind::AssocConst
+                                | DefKind::AnonConst
+                                | DefKind::ConstParam
+                                | DefKind::Ctor(..)
+                                | DefKind::Fn
+                                | DefKind::AssocFn,
+                            _
+                        ) | Res::SelfCtor(_)
+                    ) => {},
 
-            if let ExprKind::Path(p) = &e.kind {
-                if self.cx.qpath_res(p, e.hir_id) == self.res {
-                    self.found = true;
-                }
-            } else {
-                walk_expr(self, e);
+                ExprKind::AddrOf(..)
+                | ExprKind::Array(_)
+                | ExprKind::Block(..)
+                | ExprKind::Cast(..)
+                | ExprKind::DropTemps(_)
+                | ExprKind::Field(..)
+                | ExprKind::If(..)
+                | ExprKind::Let(..)
+                | ExprKind::Lit(_)
+                | ExprKind::Match(..)
+                | ExprKind::Repeat(..)
+                | ExprKind::Struct(..)
+                | ExprKind::Tup(_)
+                | ExprKind::Type(..) => (),
+
+                _ => {
+                    self.is_const = false;
+                    return;
+                },
             }
+            walk_expr(self, e);
         }
     }
 
-    let mut v = V { cx, res, found: false };
-    v.visit_expr(&cx.tcx.hir().body(body).value);
-    v.found
+    let mut v = V { cx, is_const: true };
+    v.visit_expr(e);
+    v.is_const
 }
 
-/// Calls the given function for each usage of the given local.
-pub fn for_each_local_usage<'tcx, B>(
-    cx: &LateContext<'tcx>,
-    visitable: impl Visitable<'tcx>,
-    id: HirId,
-    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
-) -> ControlFlow<B> {
-    struct V<'tcx, B, F> {
-        map: Map<'tcx>,
-        id: HirId,
-        f: F,
-        res: ControlFlow<B>,
+/// Checks if the given expression performs an unsafe operation outside of an unsafe block.
+pub fn is_expr_unsafe(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
+    struct V<'a, 'tcx> {
+        cx: &'a LateContext<'tcx>,
+        is_unsafe: bool,
     }
-    impl<'tcx, B, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>> Visitor<'tcx> for V<'tcx, B, F> {
+    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
         type Map = Map<'tcx>;
         fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-            NestedVisitorMap::OnlyBodies(self.map)
+            NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
         }
-
         fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
-            if self.res.is_continue() {
-                if path_to_local_id(e, self.id) {
-                    self.res = (self.f)(e);
-                } else {
-                    walk_expr(self, e);
-                }
+            if self.is_unsafe {
+                return;
+            }
+            match e.kind {
+                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => {
+                    self.is_unsafe = true;
+                },
+                ExprKind::MethodCall(..)
+                    if self
+                        .cx
+                        .typeck_results()
+                        .type_dependent_def_id(e.hir_id)
+                        .map_or(false, |id| self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe) =>
+                {
+                    self.is_unsafe = true;
+                },
+                ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
+                    ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
+                    ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
+                    _ => walk_expr(self, e),
+                },
+                ExprKind::Path(ref p)
+                    if self
+                        .cx
+                        .qpath_res(p, e.hir_id)
+                        .opt_def_id()
+                        .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
+                {
+                    self.is_unsafe = true;
+                },
+                _ => walk_expr(self, e),
+            }
+        }
+        fn visit_block(&mut self, b: &'tcx Block<'_>) {
+            if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
+                walk_block(self, b);
+            }
+        }
+        fn visit_nested_item(&mut self, id: ItemId) {
+            if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind {
+                self.is_unsafe = i.unsafety == Unsafety::Unsafe;
             }
         }
     }
-
-    let mut v = V {
-        map: cx.tcx.hir(),
-        id,
-        f,
-        res: ControlFlow::CONTINUE,
-    };
-    visitable.visit(&mut v);
-    v.res
-}
-
-/// Checks if the given local is used.
-pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
-    for_each_local_usage(cx, visitable, id, |_| ControlFlow::BREAK).is_break()
+    let mut v = V { cx, is_unsafe: false };
+    v.visit_expr(e);
+    v.is_unsafe
 }
index bd32696d6dbda7d15b4b6975a6195f673d2f76c7..cf16a1d5d3dcf5b09538f3d55da4dd6f3e22341d 100644 (file)
@@ -157,7 +157,7 @@ Manually testing against an example file can be useful if you have added some
 your local modifications, run
 
 ```
-env __CLIPPY_INTERNAL_TESTS=true cargo run --bin clippy-driver -- -L ./target/debug input.rs
+cargo dev lint input.rs
 ```
 
 from the working copy root. With tests in place, let's have a look at
@@ -189,6 +189,7 @@ declare_clippy_lint! {
     /// ```rust
     /// // example code
     /// ```
+    #[clippy::version = "1.29.0"]
     pub FOO_FUNCTIONS,
     pedantic,
     "function named `foo`, which is not a descriptive name"
@@ -199,6 +200,10 @@ declare_clippy_lint! {
   section. This is the default documentation style and will be displayed
   [like this][example_lint_page]. To render and open this documentation locally
   in a browser, run `cargo dev serve`.
+* The `#[clippy::version]` attribute will be rendered as part of the lint documentation.
+  The value should be set to the current Rust version that the lint is developed in,
+  it can be retrieved by running `rustc -vV` in the rust-clippy directory. The version
+  is listed under *release*. (Use the version without the `-nightly`) suffix.
 * `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the
   [lint naming guidelines][lint_naming] here when naming your lint.
   In short, the name should state the thing that is being checked for and
@@ -503,6 +508,7 @@ declare_clippy_lint! {
     /// // Good
     /// Insert a short example of improved code that doesn't trigger the lint
     /// ```
+    #[clippy::version = "1.29.0"]
     pub FOO_FUNCTIONS,
     pedantic,
     "function named `foo`, which is not a descriptive name"
@@ -634,7 +640,7 @@ in the following steps:
 Here are some pointers to things you are likely going to need for every lint:
 
 * [Clippy utils][utils] - Various helper functions. Maybe the function you need
-  is already in here (`implements_trait`, `match_def_path`, `snippet`, etc)
+  is already in here ([`is_type_diagnostic_item`], [`implements_trait`], [`snippet`], etc)
 * [Clippy diagnostics][diagnostics]
 * [The `if_chain` macro][if_chain]
 * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
@@ -660,7 +666,10 @@ documentation currently. This is unfortunate, but in most cases you can probably
 get away with copying things from existing similar lints. If you are stuck,
 don't hesitate to ask on [Zulip] or in the issue/PR.
 
-[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
+[utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html
+[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html
+[`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html
+[`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html
 [if_chain]: https://docs.rs/if_chain/*/if_chain/
 [from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
 [in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html
index 115848c48044ce19000414b2923ba5f1fc5f3e4e..0cbad2c09249c52275577d32db4b05c8a4741c73 100644 (file)
@@ -32,7 +32,7 @@ bullet points might be helpful:
   need to check out the Rust release tag of the stable release.
   [Link][rust_stable_tools]
 
-Usually you want to wirte the changelog of the **upcoming stable release**. Make
+Usually you want to write the changelog of the **upcoming stable release**. Make
 sure though, that `beta` was already branched in the Rust repository.
 
 To find the commit hash, issue the following command when in a `rust-lang/rust` checkout:
index 1a6b7c8cb47a9db623f64fe5868d29d5f1dd5a40..c7e51d53f511d9e91c044e0c985c9d42ab41b672 100644 (file)
@@ -4,10 +4,11 @@ You may need following tooltips to catch up with common operations.
 
 - [Common tools for writing lints](#common-tools-for-writing-lints)
   - [Retrieving the type of an expression](#retrieving-the-type-of-an-expression)
-  - [Checking if an expression is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
+  - [Checking if an expr is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
+  - [Checking for a specific type](#checking-for-a-specific-type)
   - [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
   - [Checking if a type defines a specific method](#checking-if-a-type-defines-a-specific-method)
-  - [Dealing with macros](#dealing-with-macros)
+  - [Dealing with macros](#dealing-with-macros-and-expansions)
 
 Useful Rustc dev guide links:
 - [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation)
@@ -15,7 +16,7 @@ Useful Rustc dev guide links:
 - [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
 - [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
 
-# Retrieving the type of an expression
+## Retrieving the type of an expression
 
 Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions:
 
@@ -54,7 +55,7 @@ Two noticeable items here:
   created by type checking step, it includes useful information such as types
   of expressions, ways to resolve methods and so on.
 
-# Checking if an expr is calling a specific method
+## Checking if an expr is calling a specific method
 
 Starting with an `expr`, you can check whether it is calling a specific method `some_method`:
 
@@ -63,9 +64,11 @@ impl LateLintPass<'_> for MyStructLint {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if_chain! {
             // Check our expr is calling a method
-            if let hir::ExprKind::MethodCall(path, _, _args, _) = &expr.kind;
+            if let hir::ExprKind::MethodCall(path, _, [_self_arg, ..], _) = &expr.kind;
             // Check the name of this method is `some_method`
             if path.ident.name == sym!(some_method);
+            // Optionally, check the type of the self argument.
+            // - See "Checking for a specific type"
             then {
                 // ...
             }
@@ -74,7 +77,45 @@ impl LateLintPass<'_> for MyStructLint {
 }
 ```
 
-# Checking if a type implements a specific trait
+## Checking for a specific type
+
+There are three ways to check if an expression type is a specific type we want to check for.
+All of these methods only check for the base type, generic arguments have to be checked separately.
+
+```rust
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
+use clippy_utils::{paths, match_def_path};
+use rustc_span::symbol::sym;
+use rustc_hir::LangItem;
+
+impl LateLintPass<'_> for MyStructLint {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        // Getting the expression type
+        let ty = cx.typeck_results().expr_ty(expr);
+
+        // 1. Using diagnostic items
+        // The last argument is the diagnostic item to check for
+        if is_type_diagnostic_item(cx, ty, sym::Option) {
+            // The type is an `Option`
+        }
+
+        // 2. Using lang items
+        if is_type_lang_item(cx, ty, LangItem::RangeFull) {
+            // The type is a full range like `.drain(..)`
+        }
+
+        // 3. Using the type path
+        // This method should be avoided if possible
+        if match_def_path(cx, def_id, &paths::RESULT) {
+            // The type is a `core::result::Result`
+        }
+    }
+}
+```
+
+Prefer using diagnostic items and lang items where possible.
+
+## Checking if a type implements a specific trait
 
 There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither.
 
@@ -102,6 +143,7 @@ impl LateLintPass<'_> for MyStructLint {
 
         // 3. Using the type path with the expression
         // we use `match_trait_method` function from Clippy's utils
+        // (This method should be avoided if possible)
         if match_trait_method(cx, expr, &paths::INTO) {
             // `expr` implements `Into` trait
         }
@@ -114,7 +156,7 @@ impl LateLintPass<'_> for MyStructLint {
 We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate.
 A list of defined paths for Clippy can be found in [paths.rs][paths]
 
-# Checking if a type defines a specific method
+## Checking if a type defines a specific method
 
 To check if our type defines a method called `some_method`:
 
@@ -140,64 +182,78 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
 }
 ```
 
-# Dealing with macros
+## Dealing with macros and expansions
 
-There are several helpers in [`clippy_utils`][utils] to deal with macros:
+Keep in mind that macros are already expanded and desugaring is already applied
+to the code representation that you are working with in Clippy. This unfortunately causes a lot of
+false positives because macro expansions are "invisible" unless you actively check for them.
+Generally speaking, code with macro expansions should just be ignored by Clippy because that code can be
+dynamic in ways that are difficult or impossible to see.
+Use the following functions to deal with macros:
 
-- `in_macro()`: detect if the given span is expanded by a macro
+- `span.from_expansion()`: detects if a span is from macro expansion or desugaring.
+  Checking this is a common first step in a lint.
 
-You may want to use this for example to not start linting in any macro.
+   ```rust
+   if expr.span.from_expansion() {
+       // just forget it
+       return;
+   }
+   ```
 
-```rust
-macro_rules! foo {
-    ($param:expr) => {
-        match $param {
-            "bar" => println!("whatever"),
-            _ => ()
-        }
-    };
-}
+- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, which macro call expanded it.
+   It is sometimes useful to check if the context of two spans are equal.
 
-foo!("bar");
+   ```rust
+   // expands to `1 + 0`, but don't lint
+   1 + mac!()
+   ```
+   ```rust
+   if left.span.ctxt() != right.span.ctxt() {
+       // the coder most likely cannot modify this expression
+       return;
+   }
+   ```
+  Note: Code that is not from expansion is in the "root" context. So any spans where `from_expansion` returns `true` can
+  be assumed to have the same context. And so just using `span.from_expansion()` is often good enough.
 
-// if we lint the `match` of `foo` call and test its span
-assert_eq!(in_macro(match_span), true);
-```
 
-- `in_external_macro()`: detect if the given span is from an external macro, defined in a foreign crate
+- `in_external_macro(span)`: detect if the given span is from a macro defined in a foreign crate.
+   If you want the lint to work with macro-generated code, this is the next line of defense to avoid macros
+   not defined in the current crate. It doesn't make sense to lint code that the coder can't change.
 
-You may want to use it for example to not start linting in macros from other crates
+   You may want to use it for example to not start linting in macros from other crates
 
-```rust
-#[macro_use]
-extern crate a_crate_with_macros;
+   ```rust
+   #[macro_use]
+   extern crate a_crate_with_macros;
 
-// `foo` is defined in `a_crate_with_macros`
-foo!("bar");
+   // `foo` is defined in `a_crate_with_macros`
+   foo!("bar");
 
-// if we lint the `match` of `foo` call and test its span
-assert_eq!(in_external_macro(cx.sess(), match_span), true);
-```
+   // if we lint the `match` of `foo` call and test its span
+   assert_eq!(in_external_macro(cx.sess(), match_span), true);
+   ```
 
 - `differing_macro_contexts()`: returns true if the two given spans are not from the same context
 
-```rust
-macro_rules! m {
-    ($a:expr, $b:expr) => {
-        if $a.is_some() {
-            $b;
-        }
-    }
-}
+   ```rust
+   macro_rules! m {
+       ($a:expr, $b:expr) => {
+           if $a.is_some() {
+               $b;
+           }
+       }
+   }
 
-let x: Option<u32> = Some(42);
-m!(x, x.unwrap());
+   let x: Option<u32> = Some(42);
+   m!(x, x.unwrap());
 
-// These spans are not from the same context
-// x.is_some() is from inside the macro
-// x.unwrap() is from outside the macro
-assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
-```
+   // These spans are not from the same context
+   // x.is_some() is from inside the macro
+   // x.unwrap() is from outside the macro
+   assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
+   ```
 
 [TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html
 [TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
@@ -207,4 +263,3 @@ assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
 [TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
 [pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty
 [paths]: ../clippy_utils/src/paths.rs
-[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
index 97d3794fb84f0a951de6acd2e8a0e3eced02d357..41de2576e28336ff83bd3904f3398693abd8be14 100644 (file)
@@ -563,7 +563,7 @@ fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
     }
 }
 
-/// Generate a short list of occuring lints-types and their count
+/// Generate a short list of occurring lints-types and their count
 fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) {
     // count lint type occurrences
     let mut counter: HashMap<&String, usize> = HashMap::new();
@@ -718,7 +718,7 @@ pub fn main() {
             // quarter of the time which might result in a longer wall clock runtime
 
             // This helps when we check many small crates with dep-trees that don't have a lot of branches in
-            // order to achive some kind of parallelism
+            // order to achieve some kind of parallelism
 
             // by default, use a single thread
             let num_cpus = config.max_jobs;
index 09554c08987b1ae66902243826bcfec6b063c91f..27969b0d655dd738de563cb53c94165a682e9660 100644 (file)
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-11-04"
-components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
+channel = "nightly-2021-12-02"
+components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index 0c82f37d6a22e32572c15956bdf6ecd7e0c32d80..a8aa3a76abc6c04a2bc1ec508af74ba946c96c45 100644 (file)
@@ -108,7 +108,7 @@ fn config(&mut self, config: &mut interface::Config) {
 
             let conf = clippy_lints::read_conf(sess);
             clippy_lints::register_plugins(lint_store, sess, &conf);
-            clippy_lints::register_pre_expansion_lints(lint_store);
+            clippy_lints::register_pre_expansion_lints(lint_store, sess, &conf);
             clippy_lints::register_renamed(lint_store);
         }));
 
index f25cf1d3ef56185243762a4f3296f4f7ca2eca98..a2d58491872b30813e99e0bb2685478183c6dc74 100644 (file)
@@ -28,6 +28,7 @@
     "serde",
     "serde_derive",
     "syn",
+    "parking_lot",
 ];
 
 // Test dependencies may need an `extern crate` here to ensure that they show up
@@ -41,6 +42,8 @@
 #[allow(unused_extern_crates)]
 extern crate itertools;
 #[allow(unused_extern_crates)]
+extern crate parking_lot;
+#[allow(unused_extern_crates)]
 extern crate quote;
 #[allow(unused_extern_crates)]
 extern crate syn;
index be42f1fbb2023b4bb5bd8d4e2937be64cafa6f80..383702dd439cfb46acbd9c26f516adfab5d4d61c 100644 (file)
@@ -11,10 +11,7 @@ fn fmt() {
     }
 
     // Skip this test if nightly rustfmt is unavailable
-    let rustup_output = Command::new("rustup")
-        .args(&["component", "list", "--toolchain", "nightly"])
-        .output()
-        .unwrap();
+    let rustup_output = Command::new("rustup").args(&["component", "list"]).output().unwrap();
     assert!(rustup_output.status.success());
     let component_output = String::from_utf8_lossy(&rustup_output.stdout);
     if !component_output.contains("rustfmt") {
diff --git a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs
new file mode 100644 (file)
index 0000000..31acac8
--- /dev/null
@@ -0,0 +1,87 @@
+#![deny(clippy::internal)]
+#![feature(rustc_private)]
+
+#[macro_use]
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_session;
+extern crate rustc_lint;
+
+///////////////////////
+// Valid descriptions
+///////////////////////
+declare_tool_lint! {
+    #[clippy::version = "pre 1.29.0"]
+    pub clippy::VALID_ONE,
+    Warn,
+    "One",
+    report_in_external_macro: true
+}
+
+declare_tool_lint! {
+    #[clippy::version = "1.29.0"]
+    pub clippy::VALID_TWO,
+    Warn,
+    "Two",
+    report_in_external_macro: true
+}
+
+declare_tool_lint! {
+    #[clippy::version = "1.59.0"]
+    pub clippy::VALID_THREE,
+    Warn,
+    "Three",
+    report_in_external_macro: true
+}
+
+///////////////////////
+// Invalid attributes
+///////////////////////
+declare_tool_lint! {
+    #[clippy::version = "1.2.3.4.5.6"]
+    pub clippy::INVALID_ONE,
+    Warn,
+    "One",
+    report_in_external_macro: true
+}
+
+declare_tool_lint! {
+    #[clippy::version = "I'm a string"]
+    pub clippy::INVALID_TWO,
+    Warn,
+    "Two",
+    report_in_external_macro: true
+}
+
+///////////////////////
+// Missing attribute test
+///////////////////////
+declare_tool_lint! {
+    #[clippy::version]
+    pub clippy::MISSING_ATTRIBUTE_ONE,
+    Warn,
+    "Two",
+    report_in_external_macro: true
+}
+
+declare_tool_lint! {
+    pub clippy::MISSING_ATTRIBUTE_TWO,
+    Warn,
+    "Two",
+    report_in_external_macro: true
+}
+
+#[allow(clippy::missing_clippy_version_attribute)]
+mod internal_clippy_lints {
+    declare_tool_lint! {
+        pub clippy::ALLOW_MISSING_ATTRIBUTE_ONE,
+        Warn,
+        "Two",
+        report_in_external_macro: true
+    }
+}
+
+use crate::internal_clippy_lints::ALLOW_MISSING_ATTRIBUTE_ONE;
+declare_lint_pass!(Pass2 => [VALID_ONE, VALID_TWO, VALID_THREE, INVALID_ONE, INVALID_TWO, MISSING_ATTRIBUTE_ONE, MISSING_ATTRIBUTE_TWO, ALLOW_MISSING_ATTRIBUTE_ONE]);
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr
new file mode 100644 (file)
index 0000000..9302e02
--- /dev/null
@@ -0,0 +1,73 @@
+error: this item has an invalid `clippy::version` attribute
+  --> $DIR/check_clippy_version_attribute.rs:40:1
+   |
+LL | / declare_tool_lint! {
+LL | |     #[clippy::version = "1.2.3.4.5.6"]
+LL | |     pub clippy::INVALID_ONE,
+LL | |     Warn,
+LL | |     "One",
+LL | |     report_in_external_macro: true
+LL | | }
+   | |_^
+   |
+note: the lint level is defined here
+  --> $DIR/check_clippy_version_attribute.rs:1:9
+   |
+LL | #![deny(clippy::internal)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
+   = help: please use a valid sematic version, see `doc/adding_lints.md`
+   = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this item has an invalid `clippy::version` attribute
+  --> $DIR/check_clippy_version_attribute.rs:48:1
+   |
+LL | / declare_tool_lint! {
+LL | |     #[clippy::version = "I'm a string"]
+LL | |     pub clippy::INVALID_TWO,
+LL | |     Warn,
+LL | |     "Two",
+LL | |     report_in_external_macro: true
+LL | | }
+   | |_^
+   |
+   = help: please use a valid sematic version, see `doc/adding_lints.md`
+   = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this lint is missing the `clippy::version` attribute or version value
+  --> $DIR/check_clippy_version_attribute.rs:59:1
+   |
+LL | / declare_tool_lint! {
+LL | |     #[clippy::version]
+LL | |     pub clippy::MISSING_ATTRIBUTE_ONE,
+LL | |     Warn,
+LL | |     "Two",
+LL | |     report_in_external_macro: true
+LL | | }
+   | |_^
+   |
+note: the lint level is defined here
+  --> $DIR/check_clippy_version_attribute.rs:1:9
+   |
+LL | #![deny(clippy::internal)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
+   = help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
+   = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this lint is missing the `clippy::version` attribute or version value
+  --> $DIR/check_clippy_version_attribute.rs:67:1
+   |
+LL | / declare_tool_lint! {
+LL | |     pub clippy::MISSING_ATTRIBUTE_TWO,
+LL | |     Warn,
+LL | |     "Two",
+LL | |     report_in_external_macro: true
+LL | | }
+   | |_^
+   |
+   = help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
+   = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
index 7764cc8da786115eb69d7de82ea2216decd6a1d1..a5a6f20ddd54aeee45784644c2bfe5a0f169a428 100644 (file)
@@ -1,5 +1,6 @@
 // run-rustfix
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate clippy_utils;
index bdd296db8320bac83ebab1971a9c0363c194dac5..6d783aa0786bd3117c8219975971d693cf07a170 100644 (file)
@@ -1,5 +1,6 @@
 // run-rustfix
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate clippy_utils;
index 0632b038577375f3a1c2ada471554bd5cb7303b8..558d129916078b4d4883203ec7f311dc5d41cc11 100644 (file)
@@ -1,5 +1,5 @@
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:35:9
+  --> $DIR/collapsible_span_lint_calls.rs:36:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
@@ -14,7 +14,7 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]`
 
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:38:9
+  --> $DIR/collapsible_span_lint_calls.rs:39:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_help(expr.span, help_msg);
@@ -22,7 +22,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)`
 
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:41:9
+  --> $DIR/collapsible_span_lint_calls.rs:42:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.help(help_msg);
@@ -30,7 +30,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
 
 error: this call is collspible
-  --> $DIR/collapsible_span_lint_calls.rs:44:9
+  --> $DIR/collapsible_span_lint_calls.rs:45:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_note(expr.span, note_msg);
@@ -38,7 +38,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
 
 error: this call is collspible
-  --> $DIR/collapsible_span_lint_calls.rs:47:9
+  --> $DIR/collapsible_span_lint_calls.rs:48:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.note(note_msg);
index 5b30c9d5721ca5a0dcf8c7fe0ef200c2339d50f2..5057a018300e98519207fed44c21d3e883b79f1c 100644 (file)
@@ -4,6 +4,7 @@
 // normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints"
 
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 
 fn it_looks_like_you_are_trying_to_kill_clippy() {}
 
index 053faae02ce3e0d3aa457083224bb9d32b20ba30..da29aedb2a3aeff5372fc274395fdd4d277a097d 100644 (file)
@@ -1,4 +1,5 @@
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 #[macro_use]
index 4735573a47d3fa2b93b8c9093d6d47f6624e0567..af6735f4e4d822bb4767b5b2293a838fb92ee5a7 100644 (file)
@@ -1,5 +1,5 @@
 error: the lint `TEST_LINT_DEFAULT` has the default lint description
-  --> $DIR/default_lint.rs:17:1
+  --> $DIR/default_lint.rs:18:1
    |
 LL | / declare_tool_lint! {
 LL | |     pub clippy::TEST_LINT_DEFAULT,
index 8e871707aa8f36953805aaf9780309ea45995f59..b0d89e038aa8b19b95e19b65eb142b203f4ce353 100644 (file)
@@ -1,5 +1,5 @@
 #![warn(clippy::if_chain_style)]
-#![allow(clippy::no_effect)]
+#![allow(clippy::no_effect, clippy::nonminimal_bool, clippy::missing_clippy_version_attribute)]
 
 extern crate if_chain;
 
index 9ab845a573aca66fc880008bee4c0a9f791bda90..6b7fd6efe394c0ebdf597fa5a4b6cc740fadfa61 100644 (file)
@@ -1,5 +1,6 @@
 // run-rustfix
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate rustc_span;
index a58e182971d7323e63b595a9e501f0abb63a63f6..98d7d7adad17037fbdc275cf9500582717b0bd71 100644 (file)
@@ -1,5 +1,6 @@
 // run-rustfix
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate rustc_span;
index 50c1c268eb132e9fdaaab784fae4861be227eb9e..4e99636e6839db1ac8a1d8e2684c60e969ea8667 100644 (file)
@@ -1,5 +1,5 @@
 error: interning a defined symbol
-  --> $DIR/interning_defined_symbol.rs:17:13
+  --> $DIR/interning_defined_symbol.rs:18:13
    |
 LL |     let _ = Symbol::intern("f32");
    |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32`
@@ -12,19 +12,19 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]`
 
 error: interning a defined symbol
-  --> $DIR/interning_defined_symbol.rs:20:13
+  --> $DIR/interning_defined_symbol.rs:21:13
    |
 LL |     let _ = sym!(f32);
    |             ^^^^^^^^^ help: try: `rustc_span::sym::f32`
 
 error: interning a defined symbol
-  --> $DIR/interning_defined_symbol.rs:23:13
+  --> $DIR/interning_defined_symbol.rs:24:13
    |
 LL |     let _ = Symbol::intern("proc-macro");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro`
 
 error: interning a defined symbol
-  --> $DIR/interning_defined_symbol.rs:26:13
+  --> $DIR/interning_defined_symbol.rs:27:13
    |
 LL |     let _ = Symbol::intern("self");
    |             ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::kw::SelfLower`
index a3b19c2e3949f112a9381eac6e71c8bf2c8b594c..b823ff7fe37f04d2e8763c6495ee4bbf029094c6 100644 (file)
@@ -1,4 +1,5 @@
 #![warn(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 
 mod paths {
     // Good path
index 20aa81b98a080c1b01478d2466910e4ceb5c9a5c..0a8e5427978f53d76fef2f88872ced4bb1af6b8a 100644 (file)
@@ -1,5 +1,5 @@
 error: invalid path
-  --> $DIR/invalid_paths.rs:17:5
+  --> $DIR/invalid_paths.rs:18:5
    |
 LL |     pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,7 +7,7 @@ LL |     pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
    = note: `-D clippy::invalid-paths` implied by `-D warnings`
 
 error: invalid path
-  --> $DIR/invalid_paths.rs:20:5
+  --> $DIR/invalid_paths.rs:21:5
    |
 LL |     pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index beaef79a340afad2c371352f2dda86f385c95d8c..1fd03cfe36da43b657e072f5aa31b8cfe026938c 100644 (file)
@@ -1,4 +1,5 @@
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 #[macro_use]
index e308e13da138304f34e1a2a340683f247eb8529a..de04920b8e6f6720363eaef6c7cd87314dfa1a7b 100644 (file)
@@ -1,5 +1,5 @@
 error: the lint `TEST_LINT` is not added to any `LintPass`
-  --> $DIR/lint_without_lint_pass.rs:11:1
+  --> $DIR/lint_without_lint_pass.rs:12:1
    |
 LL | / declare_tool_lint! {
 LL | |     pub clippy::TEST_LINT,
index be7b7a9af19207b78f30ba8d48f06f6f8a98ef2b..4b41ff15e80f904c907cb5ce02732c5eb34da2a1 100644 (file)
@@ -1,4 +1,5 @@
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate clippy_utils;
index bf1d67e6054a28c743603257388312221882c7f7..e3cb6b6c22eada7f40fb0edeb61261104385a7a0 100644 (file)
@@ -1,5 +1,5 @@
 error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:30:17
+  --> $DIR/match_type_on_diag_item.rs:31:17
    |
 LL |         let _ = match_type(cx, ty, &OPTION);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Option)`
@@ -12,13 +12,13 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]`
 
 error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:31:17
+  --> $DIR/match_type_on_diag_item.rs:32:17
    |
 LL |         let _ = match_type(cx, ty, &["core", "result", "Result"]);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Result)`
 
 error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:34:17
+  --> $DIR/match_type_on_diag_item.rs:35:17
    |
 LL |         let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)`
index b0b3498f057f7f7d05eb87124c4acb68a45aa7e6..bb82faf0c90477b49bfc4f4a97dcb077690a858f 100644 (file)
@@ -1,6 +1,7 @@
 // run-rustfix
 
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate rustc_hir;
index 55a3fed00d075dfa5a4d7360fa9b9f0a85ece34f..187d468b39251355fd95d67869de1223fb8add9c 100644 (file)
@@ -1,6 +1,7 @@
 // run-rustfix
 
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate rustc_hir;
index 56b6ce1f78ea4de38c7f1b1d0ffb666565c1f05a..afef696785e8b02621ffc908df3ff2a9e3108ff8 100644 (file)
@@ -1,5 +1,5 @@
 error: usage of `outer_expn().expn_data()`
-  --> $DIR/outer_expn_data.rs:24:34
+  --> $DIR/outer_expn_data.rs:25:34
    |
 LL |         let _ = expr.span.ctxt().outer_expn().expn_data();
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()`
index 95b8c6dfe89eea23435b45dfd0ae6386ae34367a..4f5336663a8f72d9119429fc5b7ae7a277ff6c1a 100644 (file)
@@ -1,7 +1,11 @@
 // run-rustfix
 #![feature(rustc_private)]
 #![deny(clippy::internal)]
-#![allow(clippy::unnecessary_operation, unused_must_use)]
+#![allow(
+    clippy::unnecessary_operation,
+    unused_must_use,
+    clippy::missing_clippy_version_attribute
+)]
 
 extern crate rustc_span;
 
index ad6937cf60a65c71035adde5ff4ea9a6681ce63b..894aa1d3bc6ea64932a7d05d51d89352e1c52a86 100644 (file)
@@ -1,7 +1,11 @@
 // run-rustfix
 #![feature(rustc_private)]
 #![deny(clippy::internal)]
-#![allow(clippy::unnecessary_operation, unused_must_use)]
+#![allow(
+    clippy::unnecessary_operation,
+    unused_must_use,
+    clippy::missing_clippy_version_attribute
+)]
 
 extern crate rustc_span;
 
index 12e05eaa7a09ab1070681f50275dbffff46e065d..75367bf4bc51989a43d25b4c35f2114dff83fa7a 100644 (file)
@@ -1,5 +1,5 @@
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:11:5
+  --> $DIR/unnecessary_symbol_str.rs:15:5
    |
 LL |     Symbol::intern("foo").as_str() == "clippy";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy`
@@ -12,25 +12,25 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]`
 
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:12:5
+  --> $DIR/unnecessary_symbol_str.rs:16:5
    |
 LL |     Symbol::intern("foo").to_string() == "self";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower`
 
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:13:5
+  --> $DIR/unnecessary_symbol_str.rs:17:5
    |
 LL |     Symbol::intern("foo").to_ident_string() != "Self";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper`
 
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:14:5
+  --> $DIR/unnecessary_symbol_str.rs:18:5
    |
 LL |     &*Ident::empty().as_str() == "clippy";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy`
 
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:15:5
+  --> $DIR/unnecessary_symbol_str.rs:19:5
    |
 LL |     "clippy" == Ident::empty().to_string();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name`
diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/clippy.toml b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/clippy.toml
new file mode 100644 (file)
index 0000000..78c7e63
--- /dev/null
@@ -0,0 +1 @@
+max-suggested-slice-pattern-length = 8
diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs
new file mode 100644 (file)
index 0000000..21849a1
--- /dev/null
@@ -0,0 +1,23 @@
+#![deny(clippy::index_refutable_slice)]
+
+fn below_limit() {
+    let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    if let Some(slice) = slice {
+        // This would usually not be linted but is included now due to the
+        // index limit in the config file
+        println!("{}", slice[7]);
+    }
+}
+
+fn above_limit() {
+    let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    if let Some(slice) = slice {
+        // This will not be linted as 8 is above the limit
+        println!("{}", slice[8]);
+    }
+}
+
+fn main() {
+    below_limit();
+    above_limit();
+}
diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr
new file mode 100644 (file)
index 0000000..d319e65
--- /dev/null
@@ -0,0 +1,22 @@
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/index_refutable_slice.rs:5:17
+   |
+LL |     if let Some(slice) = slice {
+   |                 ^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/index_refutable_slice.rs:1:9
+   |
+LL | #![deny(clippy::index_refutable_slice)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using a slice pattern here
+   |
+LL |     if let Some([_, _, _, _, _, _, _, slice_7, ..]) = slice {
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{}", slice_7);
+   |                        ~~~~~~~
+
+error: aborting due to previous error
+
index bc41efa42a17ce1f8f14e53f48f6d048bdc9e5b5..8e104926524e16fab2789cd6c7f0826f31aacc70 100644 (file)
@@ -59,10 +59,20 @@ fn manual_strip_msrv() {
     }
 }
 
+fn check_index_refutable_slice() {
+    // This shouldn't trigger `clippy::index_refutable_slice` as the suggestion
+    // would only be valid from 1.42.0 onward
+    let slice: Option<&[u32]> = Some(&[1]);
+    if let Some(slice) = slice {
+        println!("{}", slice[0]);
+    }
+}
+
 fn main() {
     option_as_ref_deref();
     match_like_matches();
     match_same_arms();
     match_same_arms2();
     manual_strip_msrv();
+    check_index_refutable_slice();
 }
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_method/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_method/clippy.toml
deleted file mode 100644 (file)
index f1d4a46..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-disallowed-methods = [
-    # just a string is shorthand for path only
-    "std::iter::Iterator::sum",
-    # can give path and reason with an inline table
-    { path = "regex::Regex::is_match", reason = "no matching allowed" },
-    # can use an inline table but omit reason
-    { path = "regex::Regex::new" },
-]
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs
deleted file mode 100644 (file)
index 1901a99..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#![warn(clippy::disallowed_method)]
-
-extern crate regex;
-use regex::Regex;
-
-fn main() {
-    let re = Regex::new(r"ab.*c").unwrap();
-    re.is_match("abc");
-
-    let a = vec![1, 2, 3, 4];
-    a.iter().sum::<i32>();
-}
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr
deleted file mode 100644 (file)
index 3812322..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-error: use of a disallowed method `regex::Regex::new`
-  --> $DIR/conf_disallowed_method.rs:7:14
-   |
-LL |     let re = Regex::new(r"ab.*c").unwrap();
-   |              ^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: `-D clippy::disallowed-method` implied by `-D warnings`
-
-error: use of a disallowed method `regex::Regex::is_match`
-  --> $DIR/conf_disallowed_method.rs:8:5
-   |
-LL |     re.is_match("abc");
-   |     ^^^^^^^^^^^^^^^^^^
-   |
-   = note: no matching allowed (from clippy.toml)
-
-error: use of a disallowed method `std::iter::Iterator::sum`
-  --> $DIR/conf_disallowed_method.rs:11:5
-   |
-LL |     a.iter().sum::<i32>();
-   |     ^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 3 previous errors
-
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml
new file mode 100644 (file)
index 0000000..f1d4a46
--- /dev/null
@@ -0,0 +1,8 @@
+disallowed-methods = [
+    # just a string is shorthand for path only
+    "std::iter::Iterator::sum",
+    # can give path and reason with an inline table
+    { path = "regex::Regex::is_match", reason = "no matching allowed" },
+    # can use an inline table but omit reason
+    { path = "regex::Regex::new" },
+]
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs
new file mode 100644 (file)
index 0000000..cb449b4
--- /dev/null
@@ -0,0 +1,12 @@
+#![warn(clippy::disallowed_methods)]
+
+extern crate regex;
+use regex::Regex;
+
+fn main() {
+    let re = Regex::new(r"ab.*c").unwrap();
+    re.is_match("abc");
+
+    let a = vec![1, 2, 3, 4];
+    a.iter().sum::<i32>();
+}
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
new file mode 100644 (file)
index 0000000..999ead1
--- /dev/null
@@ -0,0 +1,24 @@
+error: use of a disallowed method `regex::Regex::new`
+  --> $DIR/conf_disallowed_methods.rs:7:14
+   |
+LL |     let re = Regex::new(r"ab.*c").unwrap();
+   |              ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::disallowed-methods` implied by `-D warnings`
+
+error: use of a disallowed method `regex::Regex::is_match`
+  --> $DIR/conf_disallowed_methods.rs:8:5
+   |
+LL |     re.is_match("abc");
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+   = note: no matching allowed (from clippy.toml)
+
+error: use of a disallowed method `std::iter::Iterator::sum`
+  --> $DIR/conf_disallowed_methods.rs:11:5
+   |
+LL |     a.iter().sum::<i32>();
+   |     ^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_type/clippy.toml
deleted file mode 100644 (file)
index 6cb9e2e..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-disallowed-types = [
-    "std::collections::HashMap",
-    "std::sync::atomic::AtomicU32",
-    "syn::TypePath",
-    "proc_macro2::Ident",
-    "std::thread::Thread",
-    "std::time::Instant",
-    "std::io::Read",
-    "std::primitive::usize",
-    "bool",
-    # can give path and reason with an inline table
-    { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
-    # can use an inline table but omit reason
-    { path = "std::net::TcpListener" },
-]
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs
deleted file mode 100644 (file)
index 410f076..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#![warn(clippy::disallowed_type)]
-
-extern crate quote;
-extern crate syn;
-
-use std::sync as foo;
-use std::sync::atomic::AtomicU32;
-use std::time::Instant as Sneaky;
-
-struct HashMap;
-
-fn bad_return_type() -> fn() -> Sneaky {
-    todo!()
-}
-
-fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
-
-fn trait_obj(_: &dyn std::io::Read) {}
-
-fn full_and_single_path_prim(_: usize, _: bool) {}
-
-fn const_generics<const C: usize>() {}
-
-struct GenArg<const U: usize>([u8; U]);
-
-static BAD: foo::atomic::AtomicPtr<()> = foo::atomic::AtomicPtr::new(std::ptr::null_mut());
-
-fn ip(_: std::net::Ipv4Addr) {}
-
-fn listener(_: std::net::TcpListener) {}
-
-#[allow(clippy::diverging_sub_expression)]
-fn main() {
-    let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
-    let _ = Sneaky::now();
-    let _ = foo::atomic::AtomicU32::new(0);
-    static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
-    let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
-    let _ = syn::Ident::new("", todo!());
-    let _ = HashMap;
-    let _: usize = 64_usize;
-}
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr
deleted file mode 100644 (file)
index 08a400a..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-error: `std::sync::atomic::AtomicU32` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:7:1
-   |
-LL | use std::sync::atomic::AtomicU32;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: `-D clippy::disallowed-type` implied by `-D warnings`
-
-error: `std::time::Instant` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:8:1
-   |
-LL | use std::time::Instant as Sneaky;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: `std::time::Instant` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:12:33
-   |
-LL | fn bad_return_type() -> fn() -> Sneaky {
-   |                                 ^^^^^^
-
-error: `std::time::Instant` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:16:28
-   |
-LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
-   |                            ^^^^^^
-
-error: `std::sync::atomic::AtomicU32` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:16:39
-   |
-LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
-   |                                       ^^^^^^^^^^^^^^^^^^^^^^
-
-error: `std::io::Read` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:18:22
-   |
-LL | fn trait_obj(_: &dyn std::io::Read) {}
-   |                      ^^^^^^^^^^^^^
-
-error: `usize` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:20:33
-   |
-LL | fn full_and_single_path_prim(_: usize, _: bool) {}
-   |                                 ^^^^^
-
-error: `bool` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:20:43
-   |
-LL | fn full_and_single_path_prim(_: usize, _: bool) {}
-   |                                           ^^^^
-
-error: `usize` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:22:28
-   |
-LL | fn const_generics<const C: usize>() {}
-   |                            ^^^^^
-
-error: `usize` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:24:24
-   |
-LL | struct GenArg<const U: usize>([u8; U]);
-   |                        ^^^^^
-
-error: `std::net::Ipv4Addr` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:28:10
-   |
-LL | fn ip(_: std::net::Ipv4Addr) {}
-   |          ^^^^^^^^^^^^^^^^^^
-   |
-   = note: no IPv4 allowed (from clippy.toml)
-
-error: `std::net::TcpListener` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:30:16
-   |
-LL | fn listener(_: std::net::TcpListener) {}
-   |                ^^^^^^^^^^^^^^^^^^^^^
-
-error: `std::collections::HashMap` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:34:48
-   |
-LL |     let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
-   |                                                ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: `std::collections::HashMap` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:34:12
-   |
-LL |     let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: `std::time::Instant` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:35:13
-   |
-LL |     let _ = Sneaky::now();
-   |             ^^^^^^
-
-error: `std::sync::atomic::AtomicU32` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:36:13
-   |
-LL |     let _ = foo::atomic::AtomicU32::new(0);
-   |             ^^^^^^^^^^^^^^^^^^^^^^
-
-error: `std::sync::atomic::AtomicU32` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:37:17
-   |
-LL |     static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: `std::sync::atomic::AtomicU32` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:37:48
-   |
-LL |     static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
-   |                                                ^^^^^^^^^^^^^^^^^^^^^^
-
-error: `syn::TypePath` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:38:43
-   |
-LL |     let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
-   |                                           ^^^^^^^^^^^^^
-
-error: `syn::Ident` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:39:13
-   |
-LL |     let _ = syn::Ident::new("", todo!());
-   |             ^^^^^^^^^^
-
-error: `usize` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:41:12
-   |
-LL |     let _: usize = 64_usize;
-   |            ^^^^^
-
-error: aborting due to 21 previous errors
-
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml
new file mode 100644 (file)
index 0000000..6cb9e2e
--- /dev/null
@@ -0,0 +1,15 @@
+disallowed-types = [
+    "std::collections::HashMap",
+    "std::sync::atomic::AtomicU32",
+    "syn::TypePath",
+    "proc_macro2::Ident",
+    "std::thread::Thread",
+    "std::time::Instant",
+    "std::io::Read",
+    "std::primitive::usize",
+    "bool",
+    # can give path and reason with an inline table
+    { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
+    # can use an inline table but omit reason
+    { path = "std::net::TcpListener" },
+]
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs
new file mode 100644 (file)
index 0000000..7f28efd
--- /dev/null
@@ -0,0 +1,42 @@
+#![warn(clippy::disallowed_types)]
+
+extern crate quote;
+extern crate syn;
+
+use std::sync as foo;
+use std::sync::atomic::AtomicU32;
+use std::time::Instant as Sneaky;
+
+struct HashMap;
+
+fn bad_return_type() -> fn() -> Sneaky {
+    todo!()
+}
+
+fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
+
+fn trait_obj(_: &dyn std::io::Read) {}
+
+fn full_and_single_path_prim(_: usize, _: bool) {}
+
+fn const_generics<const C: usize>() {}
+
+struct GenArg<const U: usize>([u8; U]);
+
+static BAD: foo::atomic::AtomicPtr<()> = foo::atomic::AtomicPtr::new(std::ptr::null_mut());
+
+fn ip(_: std::net::Ipv4Addr) {}
+
+fn listener(_: std::net::TcpListener) {}
+
+#[allow(clippy::diverging_sub_expression)]
+fn main() {
+    let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
+    let _ = Sneaky::now();
+    let _ = foo::atomic::AtomicU32::new(0);
+    static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
+    let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
+    let _ = syn::Ident::new("", todo!());
+    let _ = HashMap;
+    let _: usize = 64_usize;
+}
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr
new file mode 100644 (file)
index 0000000..e3ece79
--- /dev/null
@@ -0,0 +1,132 @@
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:7:1
+   |
+LL | use std::sync::atomic::AtomicU32;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::disallowed-types` implied by `-D warnings`
+
+error: `std::time::Instant` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:8:1
+   |
+LL | use std::time::Instant as Sneaky;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::time::Instant` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:12:33
+   |
+LL | fn bad_return_type() -> fn() -> Sneaky {
+   |                                 ^^^^^^
+
+error: `std::time::Instant` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:16:28
+   |
+LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
+   |                            ^^^^^^
+
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:16:39
+   |
+LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
+   |                                       ^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::io::Read` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:18:22
+   |
+LL | fn trait_obj(_: &dyn std::io::Read) {}
+   |                      ^^^^^^^^^^^^^
+
+error: `usize` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:20:33
+   |
+LL | fn full_and_single_path_prim(_: usize, _: bool) {}
+   |                                 ^^^^^
+
+error: `bool` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:20:43
+   |
+LL | fn full_and_single_path_prim(_: usize, _: bool) {}
+   |                                           ^^^^
+
+error: `usize` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:22:28
+   |
+LL | fn const_generics<const C: usize>() {}
+   |                            ^^^^^
+
+error: `usize` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:24:24
+   |
+LL | struct GenArg<const U: usize>([u8; U]);
+   |                        ^^^^^
+
+error: `std::net::Ipv4Addr` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:28:10
+   |
+LL | fn ip(_: std::net::Ipv4Addr) {}
+   |          ^^^^^^^^^^^^^^^^^^
+   |
+   = note: no IPv4 allowed (from clippy.toml)
+
+error: `std::net::TcpListener` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:30:16
+   |
+LL | fn listener(_: std::net::TcpListener) {}
+   |                ^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::collections::HashMap` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:34:48
+   |
+LL |     let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
+   |                                                ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::collections::HashMap` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:34:12
+   |
+LL |     let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::time::Instant` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:35:13
+   |
+LL |     let _ = Sneaky::now();
+   |             ^^^^^^
+
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:36:13
+   |
+LL |     let _ = foo::atomic::AtomicU32::new(0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:37:17
+   |
+LL |     static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:37:48
+   |
+LL |     static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
+   |                                                ^^^^^^^^^^^^^^^^^^^^^^
+
+error: `syn::TypePath` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:38:43
+   |
+LL |     let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
+   |                                           ^^^^^^^^^^^^^
+
+error: `syn::Ident` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:39:13
+   |
+LL |     let _ = syn::Ident::new("", todo!());
+   |             ^^^^^^^^^^
+
+error: `usize` is not allowed according to config
+  --> $DIR/conf_disallowed_types.rs:41:12
+   |
+LL |     let _: usize = 64_usize;
+   |            ^^^^^
+
+error: aborting due to 21 previous errors
+
index 97bab1308aa52b12d94e269117263ef330ab1d0d..00ddbd608a7c725dbee190ad54a3cc3c2c6dd88e 100644 (file)
@@ -1,4 +1,4 @@
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `third-party` at line 5 column 1
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `third-party` at line 5 column 1
 
 error: aborting due to previous error
 
index 211d11c7c1aa33ba9c8f4342c4ecad5d50fda8f7..3125863036bb798a820f58864850fbe3ca567aad 100644 (file)
@@ -1,11 +1,11 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Cast(ref expr, ref cast_ty) = init.kind;
-    if let TyKind::Path(ref qp) = cast_ty.kind;
-    if match_qpath(qp, &["char"]);
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Cast(expr, cast_ty) = init.kind;
+    if let TyKind::Path(ref qpath) = cast_ty.kind;
+    if match_qpath(qpath, &["char"]);
     if let ExprKind::Lit(ref lit) = expr.kind;
-    if let LitKind::Int(69, _) = lit.node;
+    if let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node;
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
     if name.as_str() == "x";
     then {
index c8465cd59aaef0d028a7552e428b7ac8267eb1fc..a7335c01baa39447fc78dee031e852ed7b7f256c 100644 (file)
@@ -1,10 +1,16 @@
+// edition:2018
+
 #![allow(redundant_semicolons, clippy::no_effect)]
+#![feature(stmt_expr_attributes)]
+#![feature(async_closure)]
 
 #[rustfmt::skip]
 fn main() {
     #[clippy::author]
     {
         let x = 42i32;
+        let _t = 1f32;
+
         -x;
     };
     #[clippy::author]
@@ -12,4 +18,7 @@ fn main() {
         let expr = String::new();
         drop(expr)
     };
+
+    #[clippy::author]
+    async move || {};
 }
index 854bc28083a35b5908e81b592152b1af8f864aab..2fc4a7d1f7fe87482ebe6edaef25c15f2f350107 100644 (file)
@@ -1,39 +1,63 @@
 if_chain! {
-    if let ExprKind::Block(ref block) = expr.kind;
-    if block.stmts.len() == 2;
-    if let StmtKind::Local(ref local) = block.stmts[0].kind;
-    if let Some(ref init) = local.init;
+    if let ExprKind::Block(block, None) = expr.kind;
+    if block.stmts.len() == 3;
+    if let StmtKind::Local(local) = block.stmts[0].kind;
+    if let Some(init) = local.init;
     if let ExprKind::Lit(ref lit) = init.kind;
-    if let LitKind::Int(42, _) = lit.node;
+    if let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node;
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
     if name.as_str() == "x";
-    if let StmtKind::Semi(ref e, _) = block.stmts[1].kind
-    if let ExprKind::Unary(UnOp::Neg, ref inner) = e.kind;
-    if let ExprKind::Path(ref path) = inner.kind;
-    if match_qpath(path, &["x"]);
+    if let StmtKind::Local(local1) = block.stmts[1].kind;
+    if let Some(init1) = local1.init;
+    if let ExprKind::Lit(ref lit1) = init1.kind;
+    if let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node;
+    if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local1.pat.kind;
+    if name1.as_str() == "_t";
+    if let StmtKind::Semi(e) = block.stmts[2].kind;
+    if let ExprKind::Unary(UnOp::Neg, inner) = e.kind;
+    if let ExprKind::Path(ref qpath) = inner.kind;
+    if match_qpath(qpath, &["x"]);
     if block.expr.is_none();
     then {
         // report your lint here
     }
 }
 if_chain! {
-    if let ExprKind::Block(ref block) = expr.kind;
+    if let ExprKind::Block(block, None) = expr.kind;
     if block.stmts.len() == 1;
-    if let StmtKind::Local(ref local) = block.stmts[0].kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Call(ref func, ref args) = init.kind;
-    if let ExprKind::Path(ref path) = func.kind;
-    if match_qpath(path, &["String", "new"]);
-    if args.len() == 0;
+    if let StmtKind::Local(local) = block.stmts[0].kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Call(func, args) = init.kind;
+    if let ExprKind::Path(ref qpath) = func.kind;
+    if match_qpath(qpath, &["String", "new"]);
+    if args.is_empty();
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
     if name.as_str() == "expr";
-    if let Some(trailing_expr) = &block.expr;
-    if let ExprKind::Call(ref func1, ref args1) = trailing_expr.kind;
-    if let ExprKind::Path(ref path1) = func1.kind;
-    if match_qpath(path1, &["drop"]);
+    if let Some(trailing_expr) = block.expr;
+    if let ExprKind::Call(func1, args1) = trailing_expr.kind;
+    if let ExprKind::Path(ref qpath1) = func1.kind;
+    if match_qpath(qpath1, &["drop"]);
     if args1.len() == 1;
-    if let ExprKind::Path(ref path2) = args1[0].kind;
-    if match_qpath(path2, &["expr"]);
+    if let ExprKind::Path(ref qpath2) = args1[0].kind;
+    if match_qpath(qpath2, &["expr"]);
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind;
+    if let FnRetTy::DefaultReturn(_) = fn_decl.output;
+    let expr1 = &cx.tcx.hir().body(body_id).value;
+    if let ExprKind::Call(func, args) = expr1.kind;
+    if let ExprKind::Path(ref qpath) = func.kind;
+    if matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _));
+    if args.len() == 1;
+    if let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind;
+    if let FnRetTy::DefaultReturn(_) = fn_decl1.output;
+    let expr2 = &cx.tcx.hir().body(body_id1).value;
+    if let ExprKind::Block(block, None) = expr2.kind;
+    if block.stmts.is_empty();
+    if block.expr.is_none();
     then {
         // report your lint here
     }
index 4dccf666631a928a15e4beb142dde1eaedd36135..266312d63e50d57815e9aab022a45e884ae327bd 100644 (file)
@@ -1,14 +1,14 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Call(ref func, ref args) = init.kind;
-    if let ExprKind::Path(ref path) = func.kind;
-    if match_qpath(path, &["{{root}}", "std", "cmp", "min"]);
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Call(func, args) = init.kind;
+    if let ExprKind::Path(ref qpath) = func.kind;
+    if match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]);
     if args.len() == 2;
     if let ExprKind::Lit(ref lit) = args[0].kind;
-    if let LitKind::Int(3, _) = lit.node;
+    if let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node;
     if let ExprKind::Lit(ref lit1) = args[1].kind;
-    if let LitKind::Int(4, _) = lit1.node;
+    if let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node;
     if let PatKind::Wild = local.pat.kind;
     then {
         // report your lint here
diff --git a/src/tools/clippy/tests/ui/author/for_loop.rs b/src/tools/clippy/tests/ui/author/for_loop.rs
deleted file mode 100644 (file)
index b3dec87..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#![feature(stmt_expr_attributes)]
-
-fn main() {
-    #[clippy::author]
-    for y in 0..10 {
-        let z = y;
-    }
-}
diff --git a/src/tools/clippy/tests/ui/author/for_loop.stdout b/src/tools/clippy/tests/ui/author/for_loop.stdout
deleted file mode 100644 (file)
index 4d0e13c..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-if_chain! {
-    if let ExprKind::DropTemps(ref expr) = expr.kind;
-    if let ExprKind::Match(ref expr1, ref arms, MatchSource::ForLoopDesugar) = expr.kind;
-    if let ExprKind::Call(ref func, ref args) = expr1.kind;
-    if let ExprKind::Path(ref path) = func.kind;
-    if matches!(path, QPath::LangItem(LangItem::IntoIterIntoIter, _));
-    if args.len() == 1;
-    if let ExprKind::Struct(ref path1, ref fields, None) = args[0].kind;
-    if matches!(path1, QPath::LangItem(LangItem::Range, _));
-    if fields.len() == 2;
-    // unimplemented: field checks
-    if arms.len() == 1;
-    if let ExprKind::Loop(ref body, ref label, LoopSource::ForLoop) = arms[0].body.kind;
-    if body.stmts.len() == 1;
-    if let StmtKind::Expr(ref e, _) = body.stmts[0].kind
-    if let ExprKind::Match(ref expr2, ref arms1, MatchSource::ForLoopDesugar) = e.kind;
-    if let ExprKind::Call(ref func1, ref args1) = expr2.kind;
-    if let ExprKind::Path(ref path2) = func1.kind;
-    if matches!(path2, QPath::LangItem(LangItem::IteratorNext, _));
-    if args1.len() == 1;
-    if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref inner) = args1[0].kind;
-    if let ExprKind::Path(ref path3) = inner.kind;
-    if match_qpath(path3, &["iter"]);
-    if arms1.len() == 2;
-    if let ExprKind::Break(ref destination, None) = arms1[0].body.kind;
-    if let PatKind::Struct(ref path4, ref fields1, false) = arms1[0].pat.kind;
-    if matches!(path4, QPath::LangItem(LangItem::OptionNone, _));
-    if fields1.len() == 0;
-    // unimplemented: field checks
-    if let ExprKind::Block(ref block) = arms1[1].body.kind;
-    if block.stmts.len() == 1;
-    if let StmtKind::Local(ref local) = block.stmts[0].kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Path(ref path5) = init.kind;
-    if match_qpath(path5, &["y"]);
-    if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
-    if name.as_str() == "z";
-    if block.expr.is_none();
-    if let PatKind::Struct(ref path6, ref fields2, false) = arms1[1].pat.kind;
-    if matches!(path6, QPath::LangItem(LangItem::OptionSome, _));
-    if fields2.len() == 1;
-    // unimplemented: field checks
-    if body.expr.is_none();
-    if let PatKind::Binding(BindingAnnotation::Mutable, _, name1, None) = arms[0].pat.kind;
-    if name1.as_str() == "iter";
-    then {
-        // report your lint here
-    }
-}
index 2e9cb1466d0b5616a85770274cff4b602e7b687d..946088ab34619667e9c6e676f47b5839fd0e2944 100644 (file)
@@ -7,4 +7,11 @@ fn main() {
     } else {
         2 == 2;
     };
+
+    let a = true;
+
+    #[clippy::author]
+    if let true = a {
+    } else {
+    };
 }
index 1653de9a6f26d132e449cb48bcbff58f9fdd19d6..75ff3faf29aef6c97469970217f0732f748c25d2 100644 (file)
@@ -1,32 +1,50 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::If(ref cond, ref then, Some(ref else_)) = init.kind;
-    if let ExprKind::Block(ref block) = else_.kind;
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::If(cond, then, Some(else_expr)) = init.kind;
+    if let ExprKind::DropTemps(expr) = cond.kind;
+    if let ExprKind::Lit(ref lit) = expr.kind;
+    if let LitKind::Bool(true) = lit.node;
+    if let ExprKind::Block(block, None) = then.kind;
     if block.stmts.len() == 1;
-    if let StmtKind::Semi(ref e, _) = block.stmts[0].kind
-    if let ExprKind::Binary(ref op, ref left, ref right) = e.kind;
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Binary(op, left, right) = e.kind;
     if BinOpKind::Eq == op.node;
-    if let ExprKind::Lit(ref lit) = left.kind;
-    if let LitKind::Int(2, _) = lit.node;
-    if let ExprKind::Lit(ref lit1) = right.kind;
-    if let LitKind::Int(2, _) = lit1.node;
+    if let ExprKind::Lit(ref lit1) = left.kind;
+    if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node;
+    if let ExprKind::Lit(ref lit2) = right.kind;
+    if let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node;
     if block.expr.is_none();
-    if let ExprKind::DropTemps(ref expr) = cond.kind;
-    if let ExprKind::Lit(ref lit2) = expr.kind;
-    if let LitKind::Bool(true) = lit2.node;
-    if let ExprKind::Block(ref block1) = then.kind;
+    if let ExprKind::Block(block1, None) = else_expr.kind;
     if block1.stmts.len() == 1;
-    if let StmtKind::Semi(ref e1, _) = block1.stmts[0].kind
-    if let ExprKind::Binary(ref op1, ref left1, ref right1) = e1.kind;
+    if let StmtKind::Semi(e1) = block1.stmts[0].kind;
+    if let ExprKind::Binary(op1, left1, right1) = e1.kind;
     if BinOpKind::Eq == op1.node;
     if let ExprKind::Lit(ref lit3) = left1.kind;
-    if let LitKind::Int(1, _) = lit3.node;
+    if let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node;
     if let ExprKind::Lit(ref lit4) = right1.kind;
-    if let LitKind::Int(1, _) = lit4.node;
+    if let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node;
     if block1.expr.is_none();
     if let PatKind::Wild = local.pat.kind;
     then {
         // report your lint here
     }
 }
+if_chain! {
+    if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind;
+    if let ExprKind::Let(pat, expr1, _) = cond.kind;
+    if let PatKind::Lit(lit_expr) = pat.kind;
+    if let ExprKind::Lit(ref lit) = lit_expr.kind;
+    if let LitKind::Bool(true) = lit.node;
+    if let ExprKind::Path(ref qpath) = expr1.kind;
+    if match_qpath(qpath, &["a"]);
+    if let ExprKind::Block(block, None) = then.kind;
+    if block.stmts.is_empty();
+    if block.expr.is_none();
+    if let ExprKind::Block(block1, None) = else_expr.kind;
+    if block1.stmts.is_empty();
+    if block1.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
index 65f93f3cdc06b9aa4385f611f3342867cbc4a94b..bce4bc702733f206e11fc99e4585316dea805594 100644 (file)
@@ -1,12 +1,12 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Call(ref func, ref args) = init.kind;
-    if let ExprKind::Path(ref path) = func.kind;
-    if match_qpath(path, &["std", "mem", "transmute"]);
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Call(func, args) = init.kind;
+    if let ExprKind::Path(ref qpath) = func.kind;
+    if match_qpath(qpath, &["std", "mem", "transmute"]);
     if args.len() == 1;
-    if let ExprKind::Path(ref path1) = args[0].kind;
-    if match_qpath(path1, &["ZPTR"]);
+    if let ExprKind::Path(ref qpath1) = args[0].kind;
+    if match_qpath(qpath1, &["ZPTR"]);
     if let PatKind::Wild = local.pat.kind;
     then {
         // report your lint here
diff --git a/src/tools/clippy/tests/ui/author/loop.rs b/src/tools/clippy/tests/ui/author/loop.rs
new file mode 100644 (file)
index 0000000..d6de216
--- /dev/null
@@ -0,0 +1,36 @@
+#![feature(stmt_expr_attributes)]
+#![allow(clippy::never_loop, clippy::while_immutable_condition)]
+
+fn main() {
+    #[clippy::author]
+    for y in 0..10 {
+        let z = y;
+    }
+
+    #[clippy::author]
+    for _ in 0..10 {
+        break;
+    }
+
+    #[clippy::author]
+    'label: for _ in 0..10 {
+        break 'label;
+    }
+
+    let a = true;
+
+    #[clippy::author]
+    while a {
+        break;
+    }
+
+    #[clippy::author]
+    while let true = a {
+        break;
+    }
+
+    #[clippy::author]
+    loop {
+        break;
+    }
+}
diff --git a/src/tools/clippy/tests/ui/author/loop.stdout b/src/tools/clippy/tests/ui/author/loop.stdout
new file mode 100644 (file)
index 0000000..3d9560f
--- /dev/null
@@ -0,0 +1,113 @@
+if_chain! {
+    if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
+    if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = pat.kind;
+    if name.as_str() == "y";
+    if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+    if matches!(qpath, QPath::LangItem(LangItem::Range, _));
+    if fields.len() == 2;
+    if fields[0].ident.as_str() == "start";
+    if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
+    if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
+    if fields[1].ident.as_str() == "end";
+    if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
+    if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
+    if let ExprKind::Block(block, None) = body.kind;
+    if block.stmts.len() == 1;
+    if let StmtKind::Local(local) = block.stmts[0].kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Path(ref qpath1) = init.kind;
+    if match_qpath(qpath1, &["y"]);
+    if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local.pat.kind;
+    if name1.as_str() == "z";
+    if block.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
+    if let PatKind::Wild = pat.kind;
+    if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+    if matches!(qpath, QPath::LangItem(LangItem::Range, _));
+    if fields.len() == 2;
+    if fields[0].ident.as_str() == "start";
+    if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
+    if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
+    if fields[1].ident.as_str() == "end";
+    if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
+    if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
+    if let ExprKind::Block(block, None) = body.kind;
+    if block.stmts.len() == 1;
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if destination.label.is_none();
+    if block.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
+    if let PatKind::Wild = pat.kind;
+    if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+    if matches!(qpath, QPath::LangItem(LangItem::Range, _));
+    if fields.len() == 2;
+    if fields[0].ident.as_str() == "start";
+    if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
+    if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
+    if fields[1].ident.as_str() == "end";
+    if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
+    if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
+    if let ExprKind::Block(block, None) = body.kind;
+    if block.stmts.len() == 1;
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if let Some(label) = destination.label;
+    if label.ident.as_str() == "'label";
+    if block.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr);
+    if let ExprKind::Path(ref qpath) = condition.kind;
+    if match_qpath(qpath, &["a"]);
+    if let ExprKind::Block(block, None) = body.kind;
+    if block.stmts.len() == 1;
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if destination.label.is_none();
+    if block.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr);
+    if let PatKind::Lit(lit_expr) = let_pat.kind;
+    if let ExprKind::Lit(ref lit) = lit_expr.kind;
+    if let LitKind::Bool(true) = lit.node;
+    if let ExprKind::Path(ref qpath) = let_expr.kind;
+    if match_qpath(qpath, &["a"]);
+    if let ExprKind::Block(block, None) = if_then.kind;
+    if block.stmts.len() == 1;
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if destination.label.is_none();
+    if block.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind;
+    if body.stmts.len() == 1;
+    if let StmtKind::Semi(e) = body.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if destination.label.is_none();
+    if body.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
index 68cc2b214eb292fbee4c020d48fa1d2db09ebf20..38444a0094ca972eb1a0a732dea4ab1ac51e2ad3 100644 (file)
@@ -1,32 +1,35 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Match(ref expr, ref arms, MatchSource::Normal) = init.kind;
-    if let ExprKind::Lit(ref lit) = expr.kind;
-    if let LitKind::Int(42, _) = lit.node;
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind;
+    if let ExprKind::Lit(ref lit) = scrutinee.kind;
+    if let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node;
     if arms.len() == 3;
-    if let ExprKind::Lit(ref lit1) = arms[0].body.kind;
-    if let LitKind::Int(5, _) = lit1.node;
-    if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind
-    if let ExprKind::Lit(ref lit2) = lit_expr.kind;
-    if let LitKind::Int(16, _) = lit2.node;
-    if let ExprKind::Block(ref block) = arms[1].body.kind;
+    if let PatKind::Lit(lit_expr) = arms[0].pat.kind;
+    if let ExprKind::Lit(ref lit1) = lit_expr.kind;
+    if let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node;
+    if arms[0].guard.is_none();
+    if let ExprKind::Lit(ref lit2) = arms[0].body.kind;
+    if let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node;
+    if let PatKind::Lit(lit_expr1) = arms[1].pat.kind;
+    if let ExprKind::Lit(ref lit3) = lit_expr1.kind;
+    if let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node;
+    if arms[1].guard.is_none();
+    if let ExprKind::Block(block, None) = arms[1].body.kind;
     if block.stmts.len() == 1;
-    if let StmtKind::Local(ref local1) = block.stmts[0].kind;
-    if let Some(ref init1) = local1.init;
-    if let ExprKind::Lit(ref lit3) = init1.kind;
-    if let LitKind::Int(3, _) = lit3.node;
+    if let StmtKind::Local(local1) = block.stmts[0].kind;
+    if let Some(init1) = local1.init;
+    if let ExprKind::Lit(ref lit4) = init1.kind;
+    if let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node;
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local1.pat.kind;
     if name.as_str() == "x";
-    if let Some(trailing_expr) = &block.expr;
-    if let ExprKind::Path(ref path) = trailing_expr.kind;
-    if match_qpath(path, &["x"]);
-    if let PatKind::Lit(ref lit_expr1) = arms[1].pat.kind
-    if let ExprKind::Lit(ref lit4) = lit_expr1.kind;
-    if let LitKind::Int(17, _) = lit4.node;
-    if let ExprKind::Lit(ref lit5) = arms[2].body.kind;
-    if let LitKind::Int(1, _) = lit5.node;
+    if let Some(trailing_expr) = block.expr;
+    if let ExprKind::Path(ref qpath) = trailing_expr.kind;
+    if match_qpath(qpath, &["x"]);
     if let PatKind::Wild = arms[2].pat.kind;
+    if arms[2].guard.is_none();
+    if let ExprKind::Lit(ref lit5) = arms[2].body.kind;
+    if let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node;
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local.pat.kind;
     if name1.as_str() == "a";
     then {
diff --git a/src/tools/clippy/tests/ui/author/repeat.rs b/src/tools/clippy/tests/ui/author/repeat.rs
new file mode 100644 (file)
index 0000000..d8e9d58
--- /dev/null
@@ -0,0 +1,5 @@
+#[allow(clippy::no_effect)]
+fn main() {
+    #[clippy::author]
+    [1_u8; 5];
+}
diff --git a/src/tools/clippy/tests/ui/author/repeat.stdout b/src/tools/clippy/tests/ui/author/repeat.stdout
new file mode 100644 (file)
index 0000000..f16350e
--- /dev/null
@@ -0,0 +1,11 @@
+if_chain! {
+    if let ExprKind::Repeat(value, length) = expr.kind;
+    if let ExprKind::Lit(ref lit) = value.kind;
+    if let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node;
+    let expr1 = &cx.tcx.hir().body(length.body).value;
+    if let ExprKind::Lit(ref lit1) = expr1.kind;
+    if let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node;
+    then {
+        // report your lint here
+    }
+}
diff --git a/src/tools/clippy/tests/ui/author/struct.rs b/src/tools/clippy/tests/ui/author/struct.rs
new file mode 100644 (file)
index 0000000..5fdf343
--- /dev/null
@@ -0,0 +1,40 @@
+#[allow(clippy::unnecessary_operation, clippy::single_match)]
+fn main() {
+    struct Test {
+        field: u32,
+    }
+
+    #[clippy::author]
+    Test {
+        field: if true { 1 } else { 0 },
+    };
+
+    let test = Test { field: 1 };
+
+    match test {
+        #[clippy::author]
+        Test { field: 1 } => {},
+        _ => {},
+    }
+
+    struct TestTuple(u32);
+
+    let test_tuple = TestTuple(1);
+
+    match test_tuple {
+        #[clippy::author]
+        TestTuple(1) => {},
+        _ => {},
+    }
+
+    struct TestMethodCall(u32);
+
+    impl TestMethodCall {
+        fn test(&self) {}
+    }
+
+    let test_method_call = TestMethodCall(1);
+
+    #[clippy::author]
+    test_method_call.test();
+}
diff --git a/src/tools/clippy/tests/ui/author/struct.stdout b/src/tools/clippy/tests/ui/author/struct.stdout
new file mode 100644 (file)
index 0000000..ded5abd
--- /dev/null
@@ -0,0 +1,64 @@
+if_chain! {
+    if let ExprKind::Struct(qpath, fields, None) = expr.kind;
+    if match_qpath(qpath, &["Test"]);
+    if fields.len() == 1;
+    if fields[0].ident.as_str() == "field";
+    if let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind;
+    if let ExprKind::DropTemps(expr1) = cond.kind;
+    if let ExprKind::Lit(ref lit) = expr1.kind;
+    if let LitKind::Bool(true) = lit.node;
+    if let ExprKind::Block(block, None) = then.kind;
+    if block.stmts.is_empty();
+    if let Some(trailing_expr) = block.expr;
+    if let ExprKind::Lit(ref lit1) = trailing_expr.kind;
+    if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node;
+    if let ExprKind::Block(block1, None) = else_expr.kind;
+    if block1.stmts.is_empty();
+    if let Some(trailing_expr1) = block1.expr;
+    if let ExprKind::Lit(ref lit2) = trailing_expr1.kind;
+    if let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node;
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind;
+    if match_qpath(qpath, &["Test"]);
+    if fields.len() == 1;
+    if fields[0].ident.as_str() == "field";
+    if let PatKind::Lit(lit_expr) = fields[0].pat.kind;
+    if let ExprKind::Lit(ref lit) = lit_expr.kind;
+    if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
+    if arm.guard.is_none();
+    if let ExprKind::Block(block, None) = arm.body.kind;
+    if block.stmts.is_empty();
+    if block.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind;
+    if match_qpath(qpath, &["TestTuple"]);
+    if fields.len() == 1;
+    if let PatKind::Lit(lit_expr) = fields[0].kind;
+    if let ExprKind::Lit(ref lit) = lit_expr.kind;
+    if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
+    if arm.guard.is_none();
+    if let ExprKind::Block(block, None) = arm.body.kind;
+    if block.stmts.is_empty();
+    if block.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind;
+    if method_name.ident.as_str() == "test";
+    if args.len() == 1;
+    if let ExprKind::Path(ref qpath) = args[0].kind;
+    if match_qpath(qpath, &["test_method_call"]);
+    then {
+        // report your lint here
+    }
+}
diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.fixed b/src/tools/clippy/tests/ui/cast_lossless_bool.fixed
new file mode 100644 (file)
index 0000000..9e2da45
--- /dev/null
@@ -0,0 +1,42 @@
+// run-rustfix
+
+#![allow(dead_code)]
+#![warn(clippy::cast_lossless)]
+
+fn main() {
+    // Test clippy::cast_lossless with casts to integer types
+    let _ = u8::from(true);
+    let _ = u16::from(true);
+    let _ = u32::from(true);
+    let _ = u64::from(true);
+    let _ = u128::from(true);
+    let _ = usize::from(true);
+
+    let _ = i8::from(true);
+    let _ = i16::from(true);
+    let _ = i32::from(true);
+    let _ = i64::from(true);
+    let _ = i128::from(true);
+    let _ = isize::from(true);
+
+    // Test with an expression wrapped in parens
+    let _ = u16::from(true | false);
+}
+
+// The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const,
+// so we skip the lint if the expression is in a const fn.
+// See #3656
+const fn abc(input: bool) -> u32 {
+    input as u32
+}
+
+// Same as the above issue. We can't suggest `::from` in const fns in impls
+mod cast_lossless_in_impl {
+    struct A;
+
+    impl A {
+        pub const fn convert(x: bool) -> u64 {
+            x as u64
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.rs b/src/tools/clippy/tests/ui/cast_lossless_bool.rs
new file mode 100644 (file)
index 0000000..b6f6c59
--- /dev/null
@@ -0,0 +1,42 @@
+// run-rustfix
+
+#![allow(dead_code)]
+#![warn(clippy::cast_lossless)]
+
+fn main() {
+    // Test clippy::cast_lossless with casts to integer types
+    let _ = true as u8;
+    let _ = true as u16;
+    let _ = true as u32;
+    let _ = true as u64;
+    let _ = true as u128;
+    let _ = true as usize;
+
+    let _ = true as i8;
+    let _ = true as i16;
+    let _ = true as i32;
+    let _ = true as i64;
+    let _ = true as i128;
+    let _ = true as isize;
+
+    // Test with an expression wrapped in parens
+    let _ = (true | false) as u16;
+}
+
+// The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const,
+// so we skip the lint if the expression is in a const fn.
+// See #3656
+const fn abc(input: bool) -> u32 {
+    input as u32
+}
+
+// Same as the above issue. We can't suggest `::from` in const fns in impls
+mod cast_lossless_in_impl {
+    struct A;
+
+    impl A {
+        pub const fn convert(x: bool) -> u64 {
+            x as u64
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.stderr b/src/tools/clippy/tests/ui/cast_lossless_bool.stderr
new file mode 100644 (file)
index 0000000..6b14833
--- /dev/null
@@ -0,0 +1,82 @@
+error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
+  --> $DIR/cast_lossless_bool.rs:8:13
+   |
+LL |     let _ = true as u8;
+   |             ^^^^^^^^^^ help: try: `u8::from(true)`
+   |
+   = note: `-D clippy::cast-lossless` implied by `-D warnings`
+
+error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
+  --> $DIR/cast_lossless_bool.rs:9:13
+   |
+LL |     let _ = true as u16;
+   |             ^^^^^^^^^^^ help: try: `u16::from(true)`
+
+error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)`
+  --> $DIR/cast_lossless_bool.rs:10:13
+   |
+LL |     let _ = true as u32;
+   |             ^^^^^^^^^^^ help: try: `u32::from(true)`
+
+error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)`
+  --> $DIR/cast_lossless_bool.rs:11:13
+   |
+LL |     let _ = true as u64;
+   |             ^^^^^^^^^^^ help: try: `u64::from(true)`
+
+error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)`
+  --> $DIR/cast_lossless_bool.rs:12:13
+   |
+LL |     let _ = true as u128;
+   |             ^^^^^^^^^^^^ help: try: `u128::from(true)`
+
+error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)`
+  --> $DIR/cast_lossless_bool.rs:13:13
+   |
+LL |     let _ = true as usize;
+   |             ^^^^^^^^^^^^^ help: try: `usize::from(true)`
+
+error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)`
+  --> $DIR/cast_lossless_bool.rs:15:13
+   |
+LL |     let _ = true as i8;
+   |             ^^^^^^^^^^ help: try: `i8::from(true)`
+
+error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)`
+  --> $DIR/cast_lossless_bool.rs:16:13
+   |
+LL |     let _ = true as i16;
+   |             ^^^^^^^^^^^ help: try: `i16::from(true)`
+
+error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)`
+  --> $DIR/cast_lossless_bool.rs:17:13
+   |
+LL |     let _ = true as i32;
+   |             ^^^^^^^^^^^ help: try: `i32::from(true)`
+
+error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)`
+  --> $DIR/cast_lossless_bool.rs:18:13
+   |
+LL |     let _ = true as i64;
+   |             ^^^^^^^^^^^ help: try: `i64::from(true)`
+
+error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)`
+  --> $DIR/cast_lossless_bool.rs:19:13
+   |
+LL |     let _ = true as i128;
+   |             ^^^^^^^^^^^^ help: try: `i128::from(true)`
+
+error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)`
+  --> $DIR/cast_lossless_bool.rs:20:13
+   |
+LL |     let _ = true as isize;
+   |             ^^^^^^^^^^^^^ help: try: `isize::from(true)`
+
+error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
+  --> $DIR/cast_lossless_bool.rs:23:13
+   |
+LL |     let _ = (true | false) as u16;
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)`
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/ice-7934-aux.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-7934-aux.rs
new file mode 100644 (file)
index 0000000..4afbf02
--- /dev/null
@@ -0,0 +1,4 @@
+fn zero() {
+    // SAFETY:
+    unsafe { 0 };
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7934.rs b/src/tools/clippy/tests/ui/crashes/ice-7934.rs
new file mode 100644 (file)
index 0000000..a4691c4
--- /dev/null
@@ -0,0 +1,7 @@
+#![warn(clippy::undocumented_unsafe_blocks)]
+#![allow(clippy::no_effect)]
+
+#[path = "auxiliary/ice-7934-aux.rs"]
+mod zero;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs
new file mode 100644 (file)
index 0000000..d3571ea
--- /dev/null
@@ -0,0 +1,14 @@
+#![no_std]
+#![feature(lang_items, start, libc)]
+#![crate_type = "lib"]
+
+use core::panic::PanicInfo;
+
+#[warn(clippy::all)]
+fn main() {
+    let mut a = 42;
+    let mut b = 1337;
+
+    a = b;
+    b = a;
+}
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr
new file mode 100644 (file)
index 0000000..48152d8
--- /dev/null
@@ -0,0 +1,12 @@
+error: this looks like you are trying to swap `a` and `b`
+  --> $DIR/no_std_swap.rs:12:5
+   |
+LL | /     a = b;
+LL | |     b = a;
+   | |_________^ help: try: `core::mem::swap(&mut a, &mut b)`
+   |
+   = note: `-D clippy::almost-swapped` implied by `-D warnings`
+   = note: or maybe you should use `core::mem::replace`?
+
+error: aborting due to previous error
+
index 31132f86edbc452ecad7166f1cb20e47e33fd22f..40345370c0494be7cfc20b845ce61be1ec5a5ab6 100644 (file)
@@ -2,183 +2,332 @@ error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:9:9
    |
 LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
-   |         ^^^^^^^ help: try: ``foo_bar``
+   |         ^^^^^^^
    |
    = note: `-D clippy::doc-markdown` implied by `-D warnings`
+help: try
+   |
+LL | /// The `foo_bar` function does _nothing_. See also foo::bar. (note the dot there)
+   |         ~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:9:51
    |
 LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
-   |                                                   ^^^^^^^^ help: try: ``foo::bar``
+   |                                                   ^^^^^^^^
+   |
+help: try
+   |
+LL | /// The foo_bar function does _nothing_. See also `foo::bar`. (note the dot there)
+   |                                                   ~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:10:83
    |
 LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not Foo::some_fun
-   |                                                                                   ^^^^^^^^^^^^^ help: try: ``Foo::some_fun``
+   |                                                                                   ^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not `Foo::some_fun`
+   |                                                                                   ~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:12:13
    |
 LL | /// Here be ::a::global:path, and _::another::global::path_.  :: is not a path though.
-   |             ^^^^^^^^^^^^^^^^ help: try: ``::a::global:path``
+   |             ^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// Here be `::a::global:path`, and _::another::global::path_.  :: is not a path though.
+   |             ~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:12:36
    |
 LL | /// Here be ::a::global:path, and _::another::global::path_.  :: is not a path though.
-   |                                    ^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``::another::global::path``
+   |                                    ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// Here be ::a::global:path, and _`::another::global::path`_.  :: is not a path though.
+   |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:13:25
    |
 LL | /// Import an item from ::awesome::global::blob:: (Intended postfix)
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``::awesome::global::blob::``
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// Import an item from `::awesome::global::blob::` (Intended postfix)
+   |                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:14:31
    |
 LL | /// These are the options for ::Cat: (Intended trailing single colon, shouldn't be linted)
-   |                               ^^^^^ help: try: ``::Cat``
+   |                               ^^^^^
+   |
+help: try
+   |
+LL | /// These are the options for `::Cat`: (Intended trailing single colon, shouldn't be linted)
+   |                               ~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:15:22
    |
 LL | /// That's not code ~NotInCodeBlock~.
-   |                      ^^^^^^^^^^^^^^ help: try: ``NotInCodeBlock``
+   |                      ^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// That's not code ~`NotInCodeBlock`~.
+   |                      ~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:16:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:30:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:37:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:51:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:74:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:78:22
    |
 LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823.
-   |                      ^^^^^^^^^^^^^^^^^^^^^ help: try: ``link_with_underscores``
+   |                      ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// This test has [a `link_with_underscores`][chunked-example] inside it. See #823.
+   |                      ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:81:21
    |
 LL | /// It can also be [inline_link2].
-   |                     ^^^^^^^^^^^^ help: try: ``inline_link2``
+   |                     ^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// It can also be [`inline_link2`].
+   |                     ~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:91:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:99:8
    |
 LL | /// ## CamelCaseThing
-   |        ^^^^^^^^^^^^^^ help: try: ``CamelCaseThing``
+   |        ^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// ## `CamelCaseThing`
+   |        ~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:102:7
    |
 LL | /// # CamelCaseThing
-   |       ^^^^^^^^^^^^^^ help: try: ``CamelCaseThing``
+   |       ^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// # `CamelCaseThing`
+   |       ~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:104:22
    |
 LL | /// Not a title #897 CamelCaseThing
-   |                      ^^^^^^^^^^^^^^ help: try: ``CamelCaseThing``
+   |                      ^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// Not a title #897 `CamelCaseThing`
+   |                      ~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:105:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:112:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:125:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:136:43
    |
 LL | /** E.g., serialization of an empty list: FooBar
-   |                                           ^^^^^^ help: try: ``FooBar``
+   |                                           ^^^^^^
+   |
+help: try
+   |
+LL | /** E.g., serialization of an empty list: `FooBar`
+   |                                           ~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:141:5
    |
 LL | And BarQuz too.
-   |     ^^^^^^ help: try: ``BarQuz``
+   |     ^^^^^^
+   |
+help: try
+   |
+LL | And `BarQuz` too.
+   |     ~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:142:1
    |
 LL | be_sure_we_got_to_the_end_of_it
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | `be_sure_we_got_to_the_end_of_it`
+   |
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:147:43
    |
 LL | /** E.g., serialization of an empty list: FooBar
-   |                                           ^^^^^^ help: try: ``FooBar``
+   |                                           ^^^^^^
+   |
+help: try
+   |
+LL | /** E.g., serialization of an empty list: `FooBar`
+   |                                           ~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:152:5
    |
 LL | And BarQuz too.
-   |     ^^^^^^ help: try: ``BarQuz``
+   |     ^^^^^^
+   |
+help: try
+   |
+LL | And `BarQuz` too.
+   |     ~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:153:1
    |
 LL | be_sure_we_got_to_the_end_of_it
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | `be_sure_we_got_to_the_end_of_it`
+   |
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:164:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:183:22
    |
 LL | /// An iterator over mycrate::Collection's values.
-   |                      ^^^^^^^^^^^^^^^^^^^ help: try: ``mycrate::Collection``
+   |                      ^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// An iterator over `mycrate::Collection`'s values.
+   |                      ~~~~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 30 previous errors
 
index 9670e5c24fb3ecbcf5a9febdca1e2a93ddf61c07..a462b98871a8810593bf2506421ebc09490ce403 100644 (file)
@@ -22,7 +22,12 @@ error: item in documentation is missing backticks
   --> $DIR/unbalanced_ticks.rs:15:32
    |
 LL | /// This paragraph is fine and should_be linted normally.
-   |                                ^^^^^^^^^ help: try: ``should_be``
+   |                                ^^^^^^^^^
+   |
+help: try
+   |
+LL | /// This paragraph is fine and `should_be` linted normally.
+   |                                ~~~~~~~~~~~
 
 error: backticks are unbalanced
   --> $DIR/unbalanced_ticks.rs:17:1
@@ -36,7 +41,12 @@ error: item in documentation is missing backticks
   --> $DIR/unbalanced_ticks.rs:30:8
    |
 LL | /// ## not_fine
-   |        ^^^^^^^^ help: try: ``not_fine``
+   |        ^^^^^^^^
+   |
+help: try
+   |
+LL | /// ## `not_fine`
+   |        ~~~~~~~~~~
 
 error: backticks are unbalanced
   --> $DIR/unbalanced_ticks.rs:32:1
@@ -58,7 +68,12 @@ error: item in documentation is missing backticks
   --> $DIR/unbalanced_ticks.rs:35:23
    |
 LL | /// - This item needs backticks_here
-   |                       ^^^^^^^^^^^^^^ help: try: ``backticks_here``
+   |                       ^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// - This item needs `backticks_here`
+   |                       ~~~~~~~~~~~~~~~~
 
 error: aborting due to 8 previous errors
 
index 81d8221bd13e01d01e15ed5b35bdbea61867c6c7..aa966761febdf949406deb52b388dd12441c28dd 100644 (file)
@@ -158,3 +158,33 @@ pub fn test() {
         }
     }
 }
+
+mod issue_7920 {
+    pub fn test() {
+        let slice = &[1, 2, 3];
+
+        let index_usize: usize = 0;
+        let mut idx_usize: usize = 0;
+
+        // should suggest `enumerate`
+        for _item in slice {
+            if idx_usize == index_usize {
+                break;
+            }
+
+            idx_usize += 1;
+        }
+
+        let index_u32: u32 = 0;
+        let mut idx_u32: u32 = 0;
+
+        // should suggest `zip`
+        for _item in slice {
+            if idx_u32 == index_u32 {
+                break;
+            }
+
+            idx_u32 += 1;
+        }
+    }
+}
index 4cbacffe87bf47ce06072b8e263ba772e609258b..9edddea651c26f01c12b1a7f774dafa0098d77ec 100644 (file)
@@ -42,5 +42,19 @@ error: the variable `count` is used as a loop counter
 LL |         for _i in 3..10 {
    |         ^^^^^^^^^^^^^^^ help: consider using: `for (count, _i) in (3..10).enumerate()`
 
-error: aborting due to 7 previous errors
+error: the variable `idx_usize` is used as a loop counter
+  --> $DIR/explicit_counter_loop.rs:170:9
+   |
+LL |         for _item in slice {
+   |         ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_usize, _item) in slice.into_iter().enumerate()`
+
+error: the variable `idx_u32` is used as a loop counter
+  --> $DIR/explicit_counter_loop.rs:182:9
+   |
+LL |         for _item in slice {
+   |         ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_u32, _item) in (0_u32..).zip(slice.into_iter())`
+   |
+   = note: `idx_u32` is of type `u32`, making it ineligible for `Iterator::enumerate`
+
+error: aborting due to 9 previous errors
 
index cea727257c430fa49215513fc2b50a7661b50202..ca747fefc6468b59b7de7e26afcfbf9d7d27830a 100644 (file)
@@ -1,6 +1,12 @@
 // run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
+/// Allow suboptimal ops in constant context
+pub const fn in_const_context(num: f64) -> f64 {
+    if num >= 0.0 { num } else { -num }
+}
+
 struct A {
     a: f64,
     b: f64,
index ba8a8f18fa23139a8f08b1767b13ceb6ebec874a..e4b606574979830bb07ca0ed5366b192afc2f165 100644 (file)
@@ -1,6 +1,12 @@
 // run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
+/// Allow suboptimal ops in constant context
+pub const fn in_const_context(num: f64) -> f64 {
+    if num >= 0.0 { num } else { -num }
+}
+
 struct A {
     a: f64,
     b: f64,
index 35af70201fada7fc081d2992ac97ac8c380fd56d..db8290423ae057e50a8d5f8f8be4d678435f8e83 100644 (file)
@@ -1,5 +1,5 @@
 error: manual implementation of `abs` method
-  --> $DIR/floating_point_abs.rs:10:5
+  --> $DIR/floating_point_abs.rs:16:5
    |
 LL |     if num >= 0.0 { num } else { -num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
@@ -7,43 +7,43 @@ LL |     if num >= 0.0 { num } else { -num }
    = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
 
 error: manual implementation of `abs` method
-  --> $DIR/floating_point_abs.rs:14:5
+  --> $DIR/floating_point_abs.rs:20:5
    |
 LL |     if 0.0 < num { num } else { -num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
 
 error: manual implementation of `abs` method
-  --> $DIR/floating_point_abs.rs:18:5
+  --> $DIR/floating_point_abs.rs:24:5
    |
 LL |     if a.a > 0.0 { a.a } else { -a.a }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.a.abs()`
 
 error: manual implementation of `abs` method
-  --> $DIR/floating_point_abs.rs:22:5
+  --> $DIR/floating_point_abs.rs:28:5
    |
 LL |     if 0.0 >= num { -num } else { num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
 
 error: manual implementation of `abs` method
-  --> $DIR/floating_point_abs.rs:26:5
+  --> $DIR/floating_point_abs.rs:32:5
    |
 LL |     if a.a < 0.0 { -a.a } else { a.a }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.a.abs()`
 
 error: manual implementation of negation of `abs` method
-  --> $DIR/floating_point_abs.rs:30:5
+  --> $DIR/floating_point_abs.rs:36:5
    |
 LL |     if num < 0.0 { num } else { -num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-num.abs()`
 
 error: manual implementation of negation of `abs` method
-  --> $DIR/floating_point_abs.rs:34:5
+  --> $DIR/floating_point_abs.rs:40:5
    |
 LL |     if 0.0 >= num { num } else { -num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-num.abs()`
 
 error: manual implementation of negation of `abs` method
-  --> $DIR/floating_point_abs.rs:39:12
+  --> $DIR/floating_point_abs.rs:45:12
    |
 LL |         a: if a.a >= 0.0 { -a.a } else { a.a },
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-a.a.abs()`
index 911700bab0040e2e4a33439c537b73cbbeb69721..169ec02f82be6d1769b2eb7581d8a907dc096d7e 100644 (file)
@@ -1,6 +1,17 @@
 // run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
+/// Allow suboptimal_ops in constant context
+pub const fn in_const_context() {
+    let a: f64 = 1234.567;
+    let b: f64 = 45.67834;
+    let c: f64 = 0.0004;
+
+    let _ = a * b + c;
+    let _ = c + a * b;
+}
+
 fn main() {
     let a: f64 = 1234.567;
     let b: f64 = 45.67834;
index d202385fc8ae76a0ee0f169852b1d410b3a76f77..5338d4fc2b7490f4f0480b69379b8d3ee3ab2c67 100644 (file)
@@ -1,6 +1,17 @@
 // run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
+/// Allow suboptimal_ops in constant context
+pub const fn in_const_context() {
+    let a: f64 = 1234.567;
+    let b: f64 = 45.67834;
+    let c: f64 = 0.0004;
+
+    let _ = a * b + c;
+    let _ = c + a * b;
+}
+
 fn main() {
     let a: f64 = 1234.567;
     let b: f64 = 45.67834;
index ac8d0c0cae068cf87162396380ce510e075d3477..e637bbf90caa24963d172aecc6baa45fa4e12e4e 100644 (file)
@@ -1,5 +1,5 @@
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:10:13
+  --> $DIR/floating_point_mul_add.rs:21:13
    |
 LL |     let _ = a * b + c;
    |             ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
@@ -7,55 +7,55 @@ LL |     let _ = a * b + c;
    = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:11:13
+  --> $DIR/floating_point_mul_add.rs:22:13
    |
 LL |     let _ = c + a * b;
    |             ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:12:13
+  --> $DIR/floating_point_mul_add.rs:23:13
    |
 LL |     let _ = a + 2.0 * 4.0;
    |             ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:13:13
+  --> $DIR/floating_point_mul_add.rs:24:13
    |
 LL |     let _ = a + 2. * 4.;
    |             ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:15:13
+  --> $DIR/floating_point_mul_add.rs:26:13
    |
 LL |     let _ = (a * b) + c;
    |             ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:16:13
+  --> $DIR/floating_point_mul_add.rs:27:13
    |
 LL |     let _ = c + (a * b);
    |             ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:17:13
+  --> $DIR/floating_point_mul_add.rs:28:13
    |
 LL |     let _ = a * b * c + d;
    |             ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:19:13
+  --> $DIR/floating_point_mul_add.rs:30:13
    |
 LL |     let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:20:13
+  --> $DIR/floating_point_mul_add.rs:31:13
    |
 LL |     let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:22:13
+  --> $DIR/floating_point_mul_add.rs:33:13
    |
 LL |     let _ = (a * a + b).sqrt();
    |             ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)`
index 92480c5db8be446f807d15087515ecab6482fdc3..a35bb1c27f35c4150b687fbe3854a36d4e3c5ece 100644 (file)
@@ -1,6 +1,13 @@
 // run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
+/// Allow suboptimal_flops in constant context
+pub const fn const_context() {
+    let x = 3f32;
+    let _ = x * 180f32 / std::f32::consts::PI;
+}
+
 fn main() {
     let x = 3f32;
     let _ = x.to_degrees();
index 062e7c3fdc17afe8d7c1aa4d9a53aa00bff0ae3c..834db4be533c046dfc4ff96680b8d7552d06385a 100644 (file)
@@ -1,6 +1,13 @@
 // run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
+/// Allow suboptimal_flops in constant context
+pub const fn const_context() {
+    let x = 3f32;
+    let _ = x * 180f32 / std::f32::consts::PI;
+}
+
 fn main() {
     let x = 3f32;
     let _ = x * 180f32 / std::f32::consts::PI;
index a6ffdca64eefea7b563eec7a75c4002f87083be1..acecddbca53bf6254503e6e1d5c07c0be3c47b33 100644 (file)
@@ -1,5 +1,5 @@
 error: conversion to degrees can be done more accurately
-  --> $DIR/floating_point_rad.rs:6:13
+  --> $DIR/floating_point_rad.rs:13:13
    |
 LL |     let _ = x * 180f32 / std::f32::consts::PI;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()`
@@ -7,7 +7,7 @@ LL |     let _ = x * 180f32 / std::f32::consts::PI;
    = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
 
 error: conversion to radians can be done more accurately
-  --> $DIR/floating_point_rad.rs:7:13
+  --> $DIR/floating_point_rad.rs:14:13
    |
 LL |     let _ = x * std::f32::consts::PI / 180f32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()`
index 54789bf93209dff66681103f80796c148d295610..3bc3a0395244cea27e73a6bf61aaf0f8690af1c0 100644 (file)
@@ -102,3 +102,14 @@ fn into_some<T>(v: T) -> Option<T> {
 fn into_none<T>() -> Option<T> {
     None
 }
+
+// Should not warn
+fn f(b: bool, v: Option<()>) -> Option<()> {
+    if b {
+        v?; // This is a potential early return, is not equivalent with `bool::then`
+
+        Some(())
+    } else {
+        None
+    }
+}
diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs
new file mode 100644 (file)
index 0000000..c2c0c52
--- /dev/null
@@ -0,0 +1,166 @@
+#![deny(clippy::index_refutable_slice)]
+
+enum SomeEnum<T> {
+    One(T),
+    Two(T),
+    Three(T),
+    Four(T),
+}
+
+fn lintable_examples() {
+    // Try with reference
+    let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    if let Some(slice) = slice {
+        println!("{}", slice[0]);
+    }
+
+    // Try with copy
+    let slice: Option<[u32; 3]> = Some([1, 2, 3]);
+    if let Some(slice) = slice {
+        println!("{}", slice[0]);
+    }
+
+    // Try with long slice and small indices
+    let slice: Option<[u32; 9]> = Some([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+    if let Some(slice) = slice {
+        println!("{}", slice[2]);
+        println!("{}", slice[0]);
+    }
+
+    // Multiple bindings
+    let slice_wrapped: SomeEnum<[u32; 3]> = SomeEnum::One([5, 6, 7]);
+    if let SomeEnum::One(slice) | SomeEnum::Three(slice) = slice_wrapped {
+        println!("{}", slice[0]);
+    }
+
+    // Two lintable slices in one if let
+    let a_wrapped: SomeEnum<[u32; 3]> = SomeEnum::One([9, 5, 1]);
+    let b_wrapped: Option<[u32; 2]> = Some([4, 6]);
+    if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) {
+        println!("{} -> {}", a[2], b[1]);
+    }
+
+    // This requires the slice values to be borrowed as the slice values can only be
+    // borrowed and `String` doesn't implement copy
+    let slice: Option<[String; 2]> = Some([String::from("1"), String::from("2")]);
+    if let Some(ref slice) = slice {
+        println!("{:?}", slice[1]);
+    }
+    println!("{:?}", slice);
+
+    // This should not suggest using the `ref` keyword as the scrutinee is already
+    // a reference
+    let slice: Option<[String; 2]> = Some([String::from("1"), String::from("2")]);
+    if let Some(slice) = &slice {
+        println!("{:?}", slice[0]);
+    }
+    println!("{:?}", slice);
+}
+
+fn slice_index_above_limit() {
+    let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+
+    if let Some(slice) = slice {
+        // Would cause a panic, IDK
+        println!("{}", slice[7]);
+    }
+}
+
+fn slice_is_used() {
+    let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    if let Some(slice) = slice {
+        println!("{:?}", slice.len());
+    }
+
+    let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    if let Some(slice) = slice {
+        println!("{:?}", slice.to_vec());
+    }
+
+    let opt: Option<[String; 2]> = Some([String::from("Hello"), String::from("world")]);
+    if let Some(slice) = opt {
+        if !slice.is_empty() {
+            println!("first: {}", slice[0]);
+        }
+    }
+}
+
+/// The slice is used by an external function and should therefore not be linted
+fn check_slice_as_arg() {
+    fn is_interesting<T>(slice: &[T; 2]) -> bool {
+        !slice.is_empty()
+    }
+
+    let slice_wrapped: Option<[String; 2]> = Some([String::from("Hello"), String::from("world")]);
+    if let Some(slice) = &slice_wrapped {
+        if is_interesting(slice) {
+            println!("This is interesting {}", slice[0]);
+        }
+    }
+    println!("{:?}", slice_wrapped);
+}
+
+fn check_slice_in_struct() {
+    #[derive(Debug)]
+    struct Wrapper<'a> {
+        inner: Option<&'a [String]>,
+        is_awesome: bool,
+    }
+
+    impl<'a> Wrapper<'a> {
+        fn is_super_awesome(&self) -> bool {
+            self.is_awesome
+        }
+    }
+
+    let inner = &[String::from("New"), String::from("World")];
+    let wrap = Wrapper {
+        inner: Some(inner),
+        is_awesome: true,
+    };
+
+    // Test 1: Field access
+    if let Some(slice) = wrap.inner {
+        if wrap.is_awesome {
+            println!("This is awesome! {}", slice[0]);
+        }
+    }
+
+    // Test 2: function access
+    if let Some(slice) = wrap.inner {
+        if wrap.is_super_awesome() {
+            println!("This is super awesome! {}", slice[0]);
+        }
+    }
+    println!("Complete wrap: {:?}", wrap);
+}
+
+/// This would be a nice additional feature to have in the future, but adding it
+/// now would make the PR too large. This is therefore only a test that we don't
+/// lint cases we can't make a reasonable suggestion for
+fn mutable_slice_index() {
+    // Mut access
+    let mut slice: Option<[String; 1]> = Some([String::from("Penguin")]);
+    if let Some(ref mut slice) = slice {
+        slice[0] = String::from("Mr. Penguin");
+    }
+    println!("Use after modification: {:?}", slice);
+
+    // Mut access on reference
+    let mut slice: Option<[String; 1]> = Some([String::from("Cat")]);
+    if let Some(slice) = &mut slice {
+        slice[0] = String::from("Lord Meow Meow");
+    }
+    println!("Use after modification: {:?}", slice);
+}
+
+/// The lint will ignore bindings with sub patterns as it would be hard
+/// to build correct suggestions for these instances :)
+fn binding_with_sub_pattern() {
+    let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    if let Some(slice @ [_, _, _]) = slice {
+        println!("{:?}", slice[2]);
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr
new file mode 100644 (file)
index 0000000..a607df9
--- /dev/null
@@ -0,0 +1,158 @@
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:13:17
+   |
+LL |     if let Some(slice) = slice {
+   |                 ^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/if_let_slice_binding.rs:1:9
+   |
+LL | #![deny(clippy::index_refutable_slice)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using a slice pattern here
+   |
+LL |     if let Some([slice_0, ..]) = slice {
+   |                 ~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{}", slice_0);
+   |                        ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:19:17
+   |
+LL |     if let Some(slice) = slice {
+   |                 ^^^^^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let Some([slice_0, ..]) = slice {
+   |                 ~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{}", slice_0);
+   |                        ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:25:17
+   |
+LL |     if let Some(slice) = slice {
+   |                 ^^^^^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let Some([slice_0, _, slice_2, ..]) = slice {
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL ~         println!("{}", slice_2);
+LL ~         println!("{}", slice_0);
+   |
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:32:26
+   |
+LL |     if let SomeEnum::One(slice) | SomeEnum::Three(slice) = slice_wrapped {
+   |                          ^^^^^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let SomeEnum::One([slice_0, ..]) | SomeEnum::Three([slice_0, ..]) = slice_wrapped {
+   |                          ~~~~~~~~~~~~~                    ~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{}", slice_0);
+   |                        ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:39:29
+   |
+LL |     if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) {
+   |                             ^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let (SomeEnum::Three([_, _, a_2, ..]), Some(b)) = (a_wrapped, b_wrapped) {
+   |                             ~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{} -> {}", a_2, b[1]);
+   |                              ~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:39:38
+   |
+LL |     if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) {
+   |                                      ^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let (SomeEnum::Three(a), Some([_, b_1, ..])) = (a_wrapped, b_wrapped) {
+   |                                      ~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{} -> {}", a[2], b_1);
+   |                                    ~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:46:21
+   |
+LL |     if let Some(ref slice) = slice {
+   |                     ^^^^^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let Some([_, ref slice_1, ..]) = slice {
+   |                 ~~~~~~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{:?}", slice_1);
+   |                          ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:54:17
+   |
+LL |     if let Some(slice) = &slice {
+   |                 ^^^^^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let Some([slice_0, ..]) = &slice {
+   |                 ~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{:?}", slice_0);
+   |                          ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:123:17
+   |
+LL |     if let Some(slice) = wrap.inner {
+   |                 ^^^^^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let Some([slice_0, ..]) = wrap.inner {
+   |                 ~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |             println!("This is awesome! {}", slice_0);
+   |                                             ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:130:17
+   |
+LL |     if let Some(slice) = wrap.inner {
+   |                 ^^^^^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let Some([slice_0, ..]) = wrap.inner {
+   |                 ~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |             println!("This is super awesome! {}", slice_0);
+   |                                                   ~~~~~~~
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs
new file mode 100644 (file)
index 0000000..406e820
--- /dev/null
@@ -0,0 +1,28 @@
+#![deny(clippy::index_refutable_slice)]
+
+extern crate if_chain;
+use if_chain::if_chain;
+
+macro_rules! if_let_slice_macro {
+    () => {
+        // This would normally be linted
+        let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+        if let Some(slice) = slice {
+            println!("{}", slice[0]);
+        }
+    };
+}
+
+fn main() {
+    // Don't lint this
+    if_let_slice_macro!();
+
+    // Do lint this
+    if_chain! {
+        let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+        if let Some(slice) = slice;
+        then {
+            println!("{}", slice[0]);
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr
new file mode 100644 (file)
index 0000000..11b1942
--- /dev/null
@@ -0,0 +1,22 @@
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/slice_indexing_in_macro.rs:23:21
+   |
+LL |         if let Some(slice) = slice;
+   |                     ^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/slice_indexing_in_macro.rs:1:9
+   |
+LL | #![deny(clippy::index_refutable_slice)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using a slice pattern here
+   |
+LL |         if let Some([slice_0, ..]) = slice;
+   |                     ~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |             println!("{}", slice_0);
+   |                            ~~~~~~~
+
+error: aborting due to previous error
+
index 39cc58cd298439d0c06756b5f68090234b9414b1..9b862133580459199a7fa746cfbef677ded7c678 100644 (file)
@@ -23,4 +23,7 @@ fn main() {
     // Issue #6808
     let arr: [u8; 64] = [0; 64];
     let _: Vec<_> = arr.to_vec();
+
+    // Issue #6703
+    let _: Vec<isize> = v.to_vec();
 }
index c2a036ec09f1e80cea582f0b49c746497e95e3ce..639f50665f2aaff6da9f7ccd07933a05f89325eb 100644 (file)
@@ -26,4 +26,7 @@ fn main() {
     // Issue #6808
     let arr: [u8; 64] = [0; 64];
     let _: Vec<_> = arr.iter().cloned().collect();
+
+    // Issue #6703
+    let _: Vec<isize> = v.iter().copied().collect();
 }
index e1df61794cecee7273dfac211f425fec0f924680..b2cc497bf433ae608a644e88f1fc9f986c033b8b 100644 (file)
@@ -28,5 +28,11 @@ error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling
 LL |     let _: Vec<_> = arr.iter().cloned().collect();
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
 
-error: aborting due to 4 previous errors
+error: called `iter().copied().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
+  --> $DIR/iter_cloned_collect.rs:31:26
+   |
+LL |     let _: Vec<isize> = v.iter().copied().collect();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
+
+error: aborting due to 5 previous errors
 
index 2d8f3c2f0e7aacf69c5d1b0808e4f38117cfa311..c5cb2eb1fe1c2e66a727e317a0a13ffa169b8df2 100644 (file)
@@ -3,7 +3,8 @@
     unused_assignments,
     clippy::similar_names,
     clippy::blacklisted_name,
-    clippy::branches_sharing_code
+    clippy::branches_sharing_code,
+    clippy::needless_late_init
 )]
 #![warn(clippy::useless_let_if_seq)]
 
index 9cf2e10a5ee56940e5a06d103a6bcd01323dbf05..271ccce681c9fe4bb5a05f6a976bb16b7a894f91 100644 (file)
@@ -1,5 +1,5 @@
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:65:5
+  --> $DIR/let_if_seq.rs:66:5
    |
 LL | /     let mut foo = 0;
 LL | |     if f() {
@@ -11,7 +11,7 @@ LL | |     }
    = note: you might not need `mut` at all
 
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:70:5
+  --> $DIR/let_if_seq.rs:71:5
    |
 LL | /     let mut bar = 0;
 LL | |     if f() {
@@ -25,7 +25,7 @@ LL | |     }
    = note: you might not need `mut` at all
 
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:78:5
+  --> $DIR/let_if_seq.rs:79:5
    |
 LL | /     let quz;
 LL | |     if f() {
@@ -36,7 +36,7 @@ LL | |     }
    | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };`
 
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:107:5
+  --> $DIR/let_if_seq.rs:108:5
    |
 LL | /     let mut baz = 0;
 LL | |     if f() {
index 88fb216a74329597eb2c0d2fe29f696b952ca30f..539d74d1d4c9afce30deff1105cabc8d6756c0d4 100644 (file)
@@ -1,5 +1,7 @@
 #![warn(clippy::let_underscore_lock)]
 
+extern crate parking_lot;
+
 fn main() {
     let m = std::sync::Mutex::new(());
     let rw = std::sync::RwLock::new(());
@@ -10,4 +12,16 @@ fn main() {
     let _ = m.try_lock();
     let _ = rw.try_read();
     let _ = rw.try_write();
+
+    use parking_lot::{lock_api::RawMutex, Mutex, RwLock};
+
+    let p_m: Mutex<()> = Mutex::const_new(RawMutex::INIT, ());
+    let _ = p_m.lock();
+
+    let p_m1 = Mutex::new(0);
+    let _ = p_m1.lock();
+
+    let p_rw = RwLock::new(0);
+    let _ = p_rw.read();
+    let _ = p_rw.write();
 }
index 5d5f6059ef13e93b51387b3a2adca96142526727..3a2bc17bf7b20556edd6ffa07a848f3fea73effc 100644 (file)
@@ -1,5 +1,5 @@
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:7:5
+  --> $DIR/let_underscore_lock.rs:9:5
    |
 LL |     let _ = m.lock();
    |     ^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     let _ = m.lock();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:8:5
+  --> $DIR/let_underscore_lock.rs:10:5
    |
 LL |     let _ = rw.read();
    |     ^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL |     let _ = rw.read();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:9:5
+  --> $DIR/let_underscore_lock.rs:11:5
    |
 LL |     let _ = rw.write();
    |     ^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL |     let _ = rw.write();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:10:5
+  --> $DIR/let_underscore_lock.rs:12:5
    |
 LL |     let _ = m.try_lock();
    |     ^^^^^^^^^^^^^^^^^^^^^
@@ -32,7 +32,7 @@ LL |     let _ = m.try_lock();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:11:5
+  --> $DIR/let_underscore_lock.rs:13:5
    |
 LL |     let _ = rw.try_read();
    |     ^^^^^^^^^^^^^^^^^^^^^^
@@ -40,12 +40,44 @@ LL |     let _ = rw.try_read();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:12:5
+  --> $DIR/let_underscore_lock.rs:14:5
    |
 LL |     let _ = rw.try_write();
    |     ^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
-error: aborting due to 6 previous errors
+error: non-binding let on a synchronization lock
+  --> $DIR/let_underscore_lock.rs:19:5
+   |
+LL |     let _ = p_m.lock();
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+  --> $DIR/let_underscore_lock.rs:22:5
+   |
+LL |     let _ = p_m1.lock();
+   |     ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+  --> $DIR/let_underscore_lock.rs:25:5
+   |
+LL |     let _ = p_rw.read();
+   |     ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+  --> $DIR/let_underscore_lock.rs:26:5
+   |
+LL |     let _ = p_rw.write();
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: aborting due to 10 previous errors
 
index 11fe06c572471cb9f95e5dad3915ceb10d638601..6c2a25c37d8d73f6a1bd5960dbe8a8db954c91f7 100644 (file)
@@ -2,7 +2,9 @@
 // [edition2018] edition:2018
 // [edition2021] edition:2021
 // run-rustfix
+
 #![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
 
 fn main() {
     let a = vec![1, 2, 3];
index 03c03472f908f0c58d64a765798dc8cc89a09fbb..77511631e449a9c4768780d531694aafdf144909 100644 (file)
@@ -1,5 +1,5 @@
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:22:5
+  --> $DIR/manual_assert.rs:24:5
    |
 LL | /     if !a.is_empty() {
 LL | |         panic!("qaqaq{:?}", a);
@@ -9,7 +9,7 @@ LL | |     }
    = note: `-D clippy::manual-assert` implied by `-D warnings`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:25:5
+  --> $DIR/manual_assert.rs:27:5
    |
 LL | /     if !a.is_empty() {
 LL | |         panic!("qwqwq");
@@ -17,7 +17,7 @@ LL | |     }
    | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:42:5
+  --> $DIR/manual_assert.rs:44:5
    |
 LL | /     if b.is_empty() {
 LL | |         panic!("panic1");
@@ -25,7 +25,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!b.is_empty(), "panic1");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:45:5
+  --> $DIR/manual_assert.rs:47:5
    |
 LL | /     if b.is_empty() && a.is_empty() {
 LL | |         panic!("panic2");
@@ -33,7 +33,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:48:5
+  --> $DIR/manual_assert.rs:50:5
    |
 LL | /     if a.is_empty() && !b.is_empty() {
 LL | |         panic!("panic3");
@@ -41,7 +41,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:51:5
+  --> $DIR/manual_assert.rs:53:5
    |
 LL | /     if b.is_empty() || a.is_empty() {
 LL | |         panic!("panic4");
@@ -49,7 +49,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:54:5
+  --> $DIR/manual_assert.rs:56:5
    |
 LL | /     if a.is_empty() || !b.is_empty() {
 LL | |         panic!("panic5");
index 11fe06c572471cb9f95e5dad3915ceb10d638601..6c2a25c37d8d73f6a1bd5960dbe8a8db954c91f7 100644 (file)
@@ -2,7 +2,9 @@
 // [edition2018] edition:2018
 // [edition2021] edition:2021
 // run-rustfix
+
 #![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
 
 fn main() {
     let a = vec![1, 2, 3];
index 03c03472f908f0c58d64a765798dc8cc89a09fbb..77511631e449a9c4768780d531694aafdf144909 100644 (file)
@@ -1,5 +1,5 @@
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:22:5
+  --> $DIR/manual_assert.rs:24:5
    |
 LL | /     if !a.is_empty() {
 LL | |         panic!("qaqaq{:?}", a);
@@ -9,7 +9,7 @@ LL | |     }
    = note: `-D clippy::manual-assert` implied by `-D warnings`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:25:5
+  --> $DIR/manual_assert.rs:27:5
    |
 LL | /     if !a.is_empty() {
 LL | |         panic!("qwqwq");
@@ -17,7 +17,7 @@ LL | |     }
    | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:42:5
+  --> $DIR/manual_assert.rs:44:5
    |
 LL | /     if b.is_empty() {
 LL | |         panic!("panic1");
@@ -25,7 +25,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!b.is_empty(), "panic1");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:45:5
+  --> $DIR/manual_assert.rs:47:5
    |
 LL | /     if b.is_empty() && a.is_empty() {
 LL | |         panic!("panic2");
@@ -33,7 +33,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:48:5
+  --> $DIR/manual_assert.rs:50:5
    |
 LL | /     if a.is_empty() && !b.is_empty() {
 LL | |         panic!("panic3");
@@ -41,7 +41,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:51:5
+  --> $DIR/manual_assert.rs:53:5
    |
 LL | /     if b.is_empty() || a.is_empty() {
 LL | |         panic!("panic4");
@@ -49,7 +49,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:54:5
+  --> $DIR/manual_assert.rs:56:5
    |
 LL | /     if a.is_empty() || !b.is_empty() {
 LL | |         panic!("panic5");
index 11fe06c572471cb9f95e5dad3915ceb10d638601..6c2a25c37d8d73f6a1bd5960dbe8a8db954c91f7 100644 (file)
@@ -2,7 +2,9 @@
 // [edition2018] edition:2018
 // [edition2021] edition:2021
 // run-rustfix
+
 #![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
 
 fn main() {
     let a = vec![1, 2, 3];
index 8713426fc8886b9223e7631a6241162c7fc8ce88..d3e0897488f0c1ce8f315c4f8e4cf367e2f6ee76 100644 (file)
@@ -2,7 +2,9 @@
 // [edition2018] edition:2018
 // [edition2021] edition:2021
 // run-rustfix
+
 #![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
 
 fn main() {
     let a = vec![1, 2, 3];
index 8cc12149403d32aa22aaba2618c187eb65ea5c20..ebf3f8cabd4706b3da545355708d039f0f693a4f 100644 (file)
@@ -47,4 +47,14 @@ fn main() {
     let _ = s.as_ref().map(|x| {
             if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
         });
+
+    // Issue #7820
+    unsafe fn f(x: u32) -> u32 {
+        x
+    }
+    unsafe {
+        let _ = Some(0).map(|x| f(x));
+    }
+    let _ = Some(0).map(|x| unsafe { f(x) });
+    let _ = Some(0).map(|x| unsafe { f(x) });
 }
index 0862b201ead4b7290f600e6b27454a8a1b72dc4a..1382d9af0aa085d5c77c4429aa0f459ba7fecb16 100644 (file)
@@ -53,4 +53,23 @@ fn main() {
         }),
         None => None,
     };
+
+    // Issue #7820
+    unsafe fn f(x: u32) -> u32 {
+        x
+    }
+    unsafe {
+        let _ = match Some(0) {
+            Some(x) => Some(f(x)),
+            None => None,
+        };
+    }
+    let _ = match Some(0) {
+        Some(x) => unsafe { Some(f(x)) },
+        None => None,
+    };
+    let _ = match Some(0) {
+        Some(x) => Some(unsafe { f(x) }),
+        None => None,
+    };
 }
index 711ff6c4a4b095dfc5da5efd75116e4b2fa5445d..d35b6252fb8704fc377666eb5d0197006db7ef59 100644 (file)
@@ -39,5 +39,35 @@ LL +             if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
 LL ~         });
    |
 
-error: aborting due to 2 previous errors
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option_2.rs:62:17
+   |
+LL |           let _ = match Some(0) {
+   |  _________________^
+LL | |             Some(x) => Some(f(x)),
+LL | |             None => None,
+LL | |         };
+   | |_________^ help: try this: `Some(0).map(|x| f(x))`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option_2.rs:67:13
+   |
+LL |       let _ = match Some(0) {
+   |  _____________^
+LL | |         Some(x) => unsafe { Some(f(x)) },
+LL | |         None => None,
+LL | |     };
+   | |_____^ help: try this: `Some(0).map(|x| unsafe { f(x) })`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option_2.rs:71:13
+   |
+LL |       let _ = match Some(0) {
+   |  _____________^
+LL | |         Some(x) => Some(unsafe { f(x) }),
+LL | |         None => None,
+LL | |     };
+   | |_____^ help: try this: `Some(0).map(|x| unsafe { f(x) })`
+
+error: aborting due to 5 previous errors
 
index 992baf1f185a710ae1d7d3ba70207821ee9044b0..d5113df569a086c4b99b514bb59e9f0f461b3865 100644 (file)
@@ -2,7 +2,7 @@
 
 #![feature(custom_inner_attributes)]
 #![warn(clippy::manual_split_once)]
-#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)]
 
 extern crate itertools;
 
@@ -38,8 +38,8 @@ fn main() {
     let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
 
     // `rsplitn` gives the results in the reverse order of `rsplit_once`
-    let _ = "key=value".rsplit_once('=').unwrap().1;
-    let _ = "key=value".rsplit_once('=').map_or("key=value", |x| x.0);
+    let _ = "key=value".rsplitn(2, '=').next().unwrap();
+    let _ = "key=value".rsplit_once('=').unwrap().0;
     let _ = "key=value".rsplit_once('=').map(|x| x.1);
     let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
 }
index 4f92ab6b812bd87fe642d43e2fceff2d963e8116..80e02952dbd07f8adf537d03f093a9eb580fb92e 100644 (file)
@@ -2,7 +2,7 @@
 
 #![feature(custom_inner_attributes)]
 #![warn(clippy::manual_split_once)]
-#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)]
 
 extern crate itertools;
 
index 7bea2303d9213ab9d49c0d7cae676ce730738f60..af9c7a2d41bff248f287da4e9f022b06a886b02a 100644 (file)
@@ -72,17 +72,11 @@ error: manual implementation of `split_once`
 LL |         let _ = s.splitn(2, "key=value").skip(1).next()?;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
 
-error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:41:13
-   |
-LL |     let _ = "key=value".rsplitn(2, '=').next().unwrap();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().1`
-
 error: manual implementation of `rsplit_once`
   --> $DIR/manual_split_once.rs:42:13
    |
 LL |     let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map_or("key=value", |x| x.0)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0`
 
 error: manual implementation of `rsplit_once`
   --> $DIR/manual_split_once.rs:43:13
@@ -102,5 +96,5 @@ error: manual implementation of `split_once`
 LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
 
-error: aborting due to 17 previous errors
+error: aborting due to 16 previous errors
 
index 845986a4eadabeee40414a90d26162cbb4ba5007..2f85e63571351cf2023b88e3de3d05e2695d0b21 100644 (file)
@@ -100,6 +100,15 @@ fn overlapping() {
         _ => (),
     }
 
+    // Issue #7816 - overlap after included range
+    match 42 {
+        5..=10 => (),
+        0..=20 => (),
+        21..=30 => (),
+        21..=40 => (),
+        _ => (),
+    }
+
     // Issue #7829
     match 0 {
         -1..=1 => (),
@@ -107,6 +116,15 @@ fn overlapping() {
         _ => (),
     }
 
+    // Only warn about the first if there are multiple overlaps
+    match 42u128 {
+        0..=0x0000_0000_0000_00ff => (),
+        0..=0x0000_0000_0000_ffff => (),
+        0..=0x0000_0000_ffff_ffff => (),
+        0..=0xffff_ffff_ffff_ffff => (),
+        _ => (),
+    }
+
     if let None = Some(42) {
         // nothing
     } else if let None = Some(42) {
index c2b3f173c2b80bc7102261d8db7d7862e643d875..b81bb1ecfae024fb1201f4ff83ae45222c4b4e3d 100644 (file)
@@ -71,5 +71,29 @@ note: overlaps with this
 LL |         ..26 => println!("..26"),
    |         ^^^^
 
-error: aborting due to 6 previous errors
+error: some ranges overlap
+  --> $DIR/match_overlapping_arm.rs:107:9
+   |
+LL |         21..=30 => (),
+   |         ^^^^^^^
+   |
+note: overlaps with this
+  --> $DIR/match_overlapping_arm.rs:108:9
+   |
+LL |         21..=40 => (),
+   |         ^^^^^^^
+
+error: some ranges overlap
+  --> $DIR/match_overlapping_arm.rs:121:9
+   |
+LL |         0..=0x0000_0000_0000_00ff => (),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: overlaps with this
+  --> $DIR/match_overlapping_arm.rs:122:9
+   |
+LL |         0..=0x0000_0000_0000_ffff => (),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
 
index f7ed72a11cf684b64f8d584cbaa990ae4aa0fa39..b2bc97f4744a5dc749ac2bed1dae58bf1efe5788 100644 (file)
@@ -19,8 +19,7 @@ fn max(self, x: u64) -> NotOrd {
 }
 
 fn main() {
-    let x;
-    x = 2usize;
+    let x = 2usize;
     min(1, max(3, x));
     min(max(3, x), 1);
     max(min(x, 1), 3);
@@ -35,9 +34,7 @@ fn main() {
     let y = 2isize;
     min(max(y, -1), 3);
 
-    let s;
-    s = "Hello";
-
+    let s = "Hello";
     min("Apple", max("Zoo", s));
     max(min(s, "Apple"), "Zoo");
 
index 9f8e26fa406f0e59c7458ec5b54ca234386abf6d..c70b77eabbd8515abd0d0bab9a1cd869166a007a 100644 (file)
@@ -1,5 +1,5 @@
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:24:5
+  --> $DIR/min_max.rs:23:5
    |
 LL |     min(1, max(3, x));
    |     ^^^^^^^^^^^^^^^^^
@@ -7,73 +7,73 @@ LL |     min(1, max(3, x));
    = note: `-D clippy::min-max` implied by `-D warnings`
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:25:5
+  --> $DIR/min_max.rs:24:5
    |
 LL |     min(max(3, x), 1);
    |     ^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:26:5
+  --> $DIR/min_max.rs:25:5
    |
 LL |     max(min(x, 1), 3);
    |     ^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:27:5
+  --> $DIR/min_max.rs:26:5
    |
 LL |     max(3, min(x, 1));
    |     ^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:29:5
+  --> $DIR/min_max.rs:28:5
    |
 LL |     my_max(3, my_min(x, 1));
    |     ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:41:5
+  --> $DIR/min_max.rs:38:5
    |
 LL |     min("Apple", max("Zoo", s));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:42:5
+  --> $DIR/min_max.rs:39:5
    |
 LL |     max(min(s, "Apple"), "Zoo");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:47:5
+  --> $DIR/min_max.rs:44:5
    |
 LL |     x.min(1).max(3);
    |     ^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:48:5
+  --> $DIR/min_max.rs:45:5
    |
 LL |     x.max(3).min(1);
    |     ^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:49:5
+  --> $DIR/min_max.rs:46:5
    |
 LL |     f.max(3f32).min(1f32);
    |     ^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:55:5
+  --> $DIR/min_max.rs:52:5
    |
 LL |     max(x.min(1), 3);
    |     ^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:58:5
+  --> $DIR/min_max.rs:55:5
    |
 LL |     s.max("Zoo").min("Apple");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:59:5
+  --> $DIR/min_max.rs:56:5
    |
 LL |     s.min("Apple").max("Zoo");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
index 8d9fc5a864d751bf30b27da1990348ab0eb5c205..c5f221220ece7374caeab1c21dc14c35e6b8751e 100644 (file)
@@ -137,6 +137,14 @@ fn unnest_or_patterns() {
     if let TS(0, x) | TS(1, x) = TS(0, 0) {}
 }
 
+#[cfg_attr(rustfmt, rustfmt_skip)]
+fn deprecated_cfg_attr() {}
+
+#[warn(clippy::cast_lossless)]
+fn int_from_bool() -> u8 {
+    true as u8
+}
+
 fn main() {
     filter_map_next();
     checked_conversion();
@@ -153,11 +161,12 @@ fn main() {
     map_unwrap_or();
     missing_const_for_fn();
     unnest_or_patterns();
+    int_from_bool();
 }
 
-mod meets_msrv {
+mod just_under_msrv {
     #![feature(custom_inner_attributes)]
-    #![clippy::msrv = "1.45.0"]
+    #![clippy::msrv = "1.44.0"]
 
     fn main() {
         let s = "hello, world!";
@@ -167,9 +176,9 @@ fn main() {
     }
 }
 
-mod just_under_msrv {
+mod meets_msrv {
     #![feature(custom_inner_attributes)]
-    #![clippy::msrv = "1.46.0"]
+    #![clippy::msrv = "1.45.0"]
 
     fn main() {
         let s = "hello, world!";
@@ -181,7 +190,7 @@ fn main() {
 
 mod just_above_msrv {
     #![feature(custom_inner_attributes)]
-    #![clippy::msrv = "1.44.0"]
+    #![clippy::msrv = "1.46.0"]
 
     fn main() {
         let s = "hello, world!";
index 360dcfb230c6563a6c5de5d5b6e322f70473dac5..6b3fdb0844b49e1a0e2dd23dfc6c0fc70dcc4942 100644 (file)
@@ -1,12 +1,12 @@
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:165:24
+  --> $DIR/min_rust_version_attr.rs:186:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::manual-strip` implied by `-D warnings`
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:164:9
+  --> $DIR/min_rust_version_attr.rs:185:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,13 +17,13 @@ LL ~             assert_eq!(<stripped>.to_uppercase(), "WORLD!");
    |
 
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:177:24
+  --> $DIR/min_rust_version_attr.rs:198:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:176:9
+  --> $DIR/min_rust_version_attr.rs:197:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index 42c2bb9f4149eb48bbf2f777e859adcc88623ee0..9e37fb9255984fd5421bcff122e237c30d98abaf 100644 (file)
@@ -1,9 +1,10 @@
 // run-rustfix
 
 #[warn(clippy::all, clippy::needless_borrow)]
-#[allow(unused_variables)]
+#[allow(unused_variables, clippy::unnecessary_mut_passed)]
 fn main() {
     let a = 5;
+    let ref_a = &a;
     let _ = x(&a); // no warning
     let _ = x(&a); // warn
 
@@ -21,11 +22,29 @@ fn main() {
         44 => &a,
         45 => {
             println!("foo");
-            &&a // FIXME: this should lint, too
+            &a
         },
         46 => &a,
+        47 => {
+            println!("foo");
+            loop {
+                println!("{}", a);
+                if a == 25 {
+                    break ref_a;
+                }
+            }
+        },
         _ => panic!(),
     };
+
+    let _ = x(&a);
+    let _ = x(&a);
+    let _ = x(&mut b);
+    let _ = x(ref_a);
+    {
+        let b = &mut b;
+        x(b);
+    }
 }
 
 #[allow(clippy::needless_borrowed_reference)]
index 31977416bc7028738fb91c94c0a3d6dd0af4c9f9..093277784beb2ea6225ecea6087ff98bd9a5130f 100644 (file)
@@ -1,9 +1,10 @@
 // run-rustfix
 
 #[warn(clippy::all, clippy::needless_borrow)]
-#[allow(unused_variables)]
+#[allow(unused_variables, clippy::unnecessary_mut_passed)]
 fn main() {
     let a = 5;
+    let ref_a = &a;
     let _ = x(&a); // no warning
     let _ = x(&&a); // warn
 
@@ -21,11 +22,29 @@ fn main() {
         44 => &a,
         45 => {
             println!("foo");
-            &&a // FIXME: this should lint, too
+            &&a
         },
         46 => &&a,
+        47 => {
+            println!("foo");
+            loop {
+                println!("{}", a);
+                if a == 25 {
+                    break &ref_a;
+                }
+            }
+        },
         _ => panic!(),
     };
+
+    let _ = x(&&&a);
+    let _ = x(&mut &&a);
+    let _ = x(&&&mut b);
+    let _ = x(&&ref_a);
+    {
+        let b = &mut b;
+        x(&b);
+    }
 }
 
 #[allow(clippy::needless_borrowed_reference)]
index 012d62e287156f850f02871bba101d930a9bbdb7..03a5b3d260e6a984179d5cbece2b9cb666393c77 100644 (file)
@@ -1,5 +1,5 @@
 error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
-  --> $DIR/needless_borrow.rs:8:15
+  --> $DIR/needless_borrow.rs:9:15
    |
 LL |     let _ = x(&&a); // warn
    |               ^^^ help: change this to: `&a`
@@ -7,16 +7,58 @@ LL |     let _ = x(&&a); // warn
    = note: `-D clippy::needless-borrow` implied by `-D warnings`
 
 error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
-  --> $DIR/needless_borrow.rs:12:13
+  --> $DIR/needless_borrow.rs:13:13
    |
 LL |     mut_ref(&mut &mut b); // warn
    |             ^^^^^^^^^^^ help: change this to: `&mut b`
 
 error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
-  --> $DIR/needless_borrow.rs:26:15
+  --> $DIR/needless_borrow.rs:25:13
+   |
+LL |             &&a
+   |             ^^^ help: change this to: `&a`
+
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:27:15
    |
 LL |         46 => &&a,
    |               ^^^ help: change this to: `&a`
 
-error: aborting due to 3 previous errors
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:33:27
+   |
+LL |                     break &ref_a;
+   |                           ^^^^^^ help: change this to: `ref_a`
+
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:40:15
+   |
+LL |     let _ = x(&&&a);
+   |               ^^^^ help: change this to: `&a`
+
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:41:15
+   |
+LL |     let _ = x(&mut &&a);
+   |               ^^^^^^^^ help: change this to: `&a`
+
+error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:42:15
+   |
+LL |     let _ = x(&&&mut b);
+   |               ^^^^^^^^ help: change this to: `&mut b`
+
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:43:15
+   |
+LL |     let _ = x(&&ref_a);
+   |               ^^^^^^^ help: change this to: `ref_a`
+
+error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:46:11
+   |
+LL |         x(&b);
+   |           ^^ help: change this to: `b`
+
+error: aborting due to 10 previous errors
 
index 2c94235b8f533279ba502e12a95c88c1372fb9c9..1f11d1f8d563c2bcb213f6924ac169512950d762 100644 (file)
@@ -76,6 +76,37 @@ fn dont_lint(string: &str) -> usize {
     }
 }
 
+mod issue7975 {
+    use super::*;
+
+    fn direct_mapping_with_used_mutable_reference() -> Vec<()> {
+        let test_vec: Vec<()> = vec![];
+        let mut vec_2: Vec<()> = vec![];
+        let mut_ref = &mut vec_2;
+        let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
+        collected_vec.into_iter().map(|_| mut_ref.push(())).collect()
+    }
+
+    fn indirectly_mapping_with_used_mutable_reference() -> Vec<()> {
+        let test_vec: Vec<()> = vec![];
+        let mut vec_2: Vec<()> = vec![];
+        let mut_ref = &mut vec_2;
+        let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
+        let iter = collected_vec.into_iter();
+        iter.map(|_| mut_ref.push(())).collect()
+    }
+
+    fn indirect_collect_after_indirect_mapping_with_used_mutable_reference() -> Vec<()> {
+        let test_vec: Vec<()> = vec![];
+        let mut vec_2: Vec<()> = vec![];
+        let mut_ref = &mut vec_2;
+        let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
+        let iter = collected_vec.into_iter();
+        let mapped_iter = iter.map(|_| mut_ref.push(()));
+        mapped_iter.collect()
+    }
+}
+
 fn allow_test() {
     #[allow(clippy::needless_collect)]
     let v = [1].iter().collect::<Vec<_>>();
diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs
new file mode 100644 (file)
index 0000000..6fdb4cc
--- /dev/null
@@ -0,0 +1,167 @@
+#![allow(unused)]
+
+fn main() {
+    let a;
+    let n = 1;
+    match n {
+        1 => a = "one",
+        _ => {
+            a = "two";
+        },
+    }
+
+    let b;
+    if n == 3 {
+        b = "four";
+    } else {
+        b = "five"
+    }
+
+    let c;
+    if let Some(n) = Some(5) {
+        c = n;
+    } else {
+        c = -50;
+    }
+
+    let d;
+    if true {
+        let temp = 5;
+        d = temp;
+    } else {
+        d = 15;
+    }
+
+    let e;
+    if true {
+        e = format!("{} {}", a, b);
+    } else {
+        e = format!("{}", c);
+    }
+
+    println!("{}", a);
+}
+
+async fn in_async() -> &'static str {
+    async fn f() -> &'static str {
+        "one"
+    }
+
+    let a;
+    let n = 1;
+    match n {
+        1 => a = f().await,
+        _ => {
+            a = "two";
+        },
+    }
+
+    a
+}
+
+const fn in_const() -> &'static str {
+    const fn f() -> &'static str {
+        "one"
+    }
+
+    let a;
+    let n = 1;
+    match n {
+        1 => a = f(),
+        _ => {
+            a = "two";
+        },
+    }
+
+    a
+}
+
+fn does_not_lint() {
+    let z;
+    if false {
+        z = 1;
+    }
+
+    let x;
+    let y;
+    if true {
+        x = 1;
+    } else {
+        y = 1;
+    }
+
+    let mut x;
+    if true {
+        x = 5;
+        x = 10 / x;
+    } else {
+        x = 2;
+    }
+
+    let x;
+    let _ = match 1 {
+        1 => x = 10,
+        _ => x = 20,
+    };
+
+    // using tuples would be possible, but not always preferable
+    let x;
+    let y;
+    if true {
+        x = 1;
+        y = 2;
+    } else {
+        x = 3;
+        y = 4;
+    }
+
+    // could match with a smarter heuristic to avoid multiple assignments
+    let x;
+    if true {
+        let mut y = 5;
+        y = 6;
+        x = y;
+    } else {
+        x = 2;
+    }
+
+    let (x, y);
+    if true {
+        x = 1;
+    } else {
+        x = 2;
+    }
+    y = 3;
+
+    macro_rules! assign {
+        ($i:ident) => {
+            $i = 1;
+        };
+    }
+    let x;
+    assign!(x);
+
+    let x;
+    if true {
+        assign!(x);
+    } else {
+        x = 2;
+    }
+
+    macro_rules! in_macro {
+        () => {
+            let x;
+            x = 1;
+
+            let x;
+            if true {
+                x = 1;
+            } else {
+                x = 2;
+            }
+        };
+    }
+    in_macro!();
+
+    println!("{}", x);
+}
diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr
new file mode 100644 (file)
index 0000000..cbb7538
--- /dev/null
@@ -0,0 +1,150 @@
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:4:5
+   |
+LL |     let a;
+   |     ^^^^^^
+   |
+   = note: `-D clippy::needless-late-init` implied by `-D warnings`
+help: declare `a` here
+   |
+LL |     let a = match n {
+   |     +++++++
+help: remove the assignments from the `match` arms
+   |
+LL ~         1 => "one",
+LL |         _ => {
+LL ~             "two"
+   |
+help: add a semicolon after the `match` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:13:5
+   |
+LL |     let b;
+   |     ^^^^^^
+   |
+help: declare `b` here
+   |
+LL |     let b = if n == 3 {
+   |     +++++++
+help: remove the assignments from the branches
+   |
+LL ~         "four"
+LL |     } else {
+LL ~         "five"
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:20:5
+   |
+LL |     let c;
+   |     ^^^^^^
+   |
+help: declare `c` here
+   |
+LL |     let c = if let Some(n) = Some(5) {
+   |     +++++++
+help: remove the assignments from the branches
+   |
+LL ~         n
+LL |     } else {
+LL ~         -50
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:27:5
+   |
+LL |     let d;
+   |     ^^^^^^
+   |
+help: declare `d` here
+   |
+LL |     let d = if true {
+   |     +++++++
+help: remove the assignments from the branches
+   |
+LL ~         temp
+LL |     } else {
+LL ~         15
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:35:5
+   |
+LL |     let e;
+   |     ^^^^^^
+   |
+help: declare `e` here
+   |
+LL |     let e = if true {
+   |     +++++++
+help: remove the assignments from the branches
+   |
+LL ~         format!("{} {}", a, b)
+LL |     } else {
+LL ~         format!("{}", c)
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:50:5
+   |
+LL |     let a;
+   |     ^^^^^^
+   |
+help: declare `a` here
+   |
+LL |     let a = match n {
+   |     +++++++
+help: remove the assignments from the `match` arms
+   |
+LL ~         1 => f().await,
+LL |         _ => {
+LL ~             "two"
+   |
+help: add a semicolon after the `match` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:67:5
+   |
+LL |     let a;
+   |     ^^^^^^
+   |
+help: declare `a` here
+   |
+LL |     let a = match n {
+   |     +++++++
+help: remove the assignments from the `match` arms
+   |
+LL ~         1 => f(),
+LL |         _ => {
+LL ~             "two"
+   |
+help: add a semicolon after the `match` expression
+   |
+LL |     };
+   |      +
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_late_init_fixable.fixed b/src/tools/clippy/tests/ui/needless_late_init_fixable.fixed
new file mode 100644 (file)
index 0000000..32d5d04
--- /dev/null
@@ -0,0 +1,38 @@
+// run-rustfix
+
+#![allow(unused, clippy::assign_op_pattern)]
+
+fn main() {
+    
+    let a = "zero";
+
+    
+    
+    let b = 1;
+    let c = 2;
+
+    
+    let d: usize = 1;
+
+    
+    let mut e = 1;
+    e = 2;
+
+    
+    let f = match 1 {
+        1 => "three",
+        _ => return,
+    }; // has semi
+
+    
+    let g: usize = if true {
+        5
+    } else {
+        panic!();
+    };
+
+    
+    let h = format!("{}", e);
+
+    println!("{}", a);
+}
diff --git a/src/tools/clippy/tests/ui/needless_late_init_fixable.rs b/src/tools/clippy/tests/ui/needless_late_init_fixable.rs
new file mode 100644 (file)
index 0000000..6bc85f6
--- /dev/null
@@ -0,0 +1,38 @@
+// run-rustfix
+
+#![allow(unused, clippy::assign_op_pattern)]
+
+fn main() {
+    let a;
+    a = "zero";
+
+    let b;
+    let c;
+    b = 1;
+    c = 2;
+
+    let d: usize;
+    d = 1;
+
+    let mut e;
+    e = 1;
+    e = 2;
+
+    let f;
+    match 1 {
+        1 => f = "three",
+        _ => return,
+    }; // has semi
+
+    let g: usize;
+    if true {
+        g = 5;
+    } else {
+        panic!();
+    }
+
+    let h;
+    h = format!("{}", e);
+
+    println!("{}", a);
+}
diff --git a/src/tools/clippy/tests/ui/needless_late_init_fixable.stderr b/src/tools/clippy/tests/ui/needless_late_init_fixable.stderr
new file mode 100644 (file)
index 0000000..a0ce4f8
--- /dev/null
@@ -0,0 +1,103 @@
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:6:5
+   |
+LL |     let a;
+   |     ^^^^^^
+   |
+   = note: `-D clippy::needless-late-init` implied by `-D warnings`
+help: declare `a` here
+   |
+LL |     let a = "zero";
+   |     ~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:9:5
+   |
+LL |     let b;
+   |     ^^^^^^
+   |
+help: declare `b` here
+   |
+LL |     let b = 1;
+   |     ~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:10:5
+   |
+LL |     let c;
+   |     ^^^^^^
+   |
+help: declare `c` here
+   |
+LL |     let c = 2;
+   |     ~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:14:5
+   |
+LL |     let d: usize;
+   |     ^^^^^^^^^^^^^
+   |
+help: declare `d` here
+   |
+LL |     let d: usize = 1;
+   |     ~~~~~~~~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:17:5
+   |
+LL |     let mut e;
+   |     ^^^^^^^^^^
+   |
+help: declare `e` here
+   |
+LL |     let mut e = 1;
+   |     ~~~~~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:21:5
+   |
+LL |     let f;
+   |     ^^^^^^
+   |
+help: declare `f` here
+   |
+LL |     let f = match 1 {
+   |     +++++++
+help: remove the assignments from the `match` arms
+   |
+LL |         1 => "three",
+   |              ~~~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:27:5
+   |
+LL |     let g: usize;
+   |     ^^^^^^^^^^^^^
+   |
+help: declare `g` here
+   |
+LL |     let g: usize = if true {
+   |     ++++++++++++++
+help: remove the assignments from the branches
+   |
+LL |         5
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:34:5
+   |
+LL |     let h;
+   |     ^^^^^^
+   |
+help: declare `h` here
+   |
+LL |     let h = format!("{}", e);
+   |     ~~~~~
+
+error: aborting due to 8 previous errors
+
index 02bf50d077abff4c523b38f8822fc1888b26095d..57c3d48c7611c2b1cad980070081cce9bb574aa2 100644 (file)
@@ -2,7 +2,7 @@ error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:23:12
    |
 LL |     return Some(to.magic?);
-   |            ^^^^^^^^^^^^^^^ help: try: `to.magic`
+   |            ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
    |
    = note: `-D clippy::needless-question-mark` implied by `-D warnings`
 
@@ -10,67 +10,67 @@ error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:31:12
    |
 LL |     return Some(to.magic?)
-   |            ^^^^^^^^^^^^^^^ help: try: `to.magic`
+   |            ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:36:5
    |
 LL |     Some(to.magic?)
-   |     ^^^^^^^^^^^^^^^ help: try: `to.magic`
+   |     ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:41:21
    |
 LL |     to.and_then(|t| Some(t.magic?))
-   |                     ^^^^^^^^^^^^^^ help: try: `t.magic`
+   |                     ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:50:9
    |
 LL |         Some(t.magic?)
-   |         ^^^^^^^^^^^^^^ help: try: `t.magic`
+   |         ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:55:12
    |
 LL |     return Ok(tr.magic?);
-   |            ^^^^^^^^^^^^^ help: try: `tr.magic`
+   |            ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:62:12
    |
 LL |     return Ok(tr.magic?)
-   |            ^^^^^^^^^^^^^ help: try: `tr.magic`
+   |            ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:66:5
    |
 LL |     Ok(tr.magic?)
-   |     ^^^^^^^^^^^^^ help: try: `tr.magic`
+   |     ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:70:21
    |
 LL |     tr.and_then(|t| Ok(t.magic?))
-   |                     ^^^^^^^^^^^^ help: try: `t.magic`
+   |                     ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:78:9
    |
 LL |         Ok(t.magic?)
-   |         ^^^^^^^^^^^^ help: try: `t.magic`
+   |         ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:85:16
    |
 LL |         return Ok(t.magic?);
-   |                ^^^^^^^^^^^^ help: try: `t.magic`
+   |                ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:120:27
    |
 LL |         || -> Option<_> { Some(Some($expr)?) }()
-   |                           ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)`
+   |                           ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)`
 ...
 LL |     let _x = some_and_qmark_in_macro!(x?);
    |              ---------------------------- in this macro invocation
index 812ce7163cd50ab215cd7130493b1e816d71ccf4..83f467c84002621244adf95dc5e8f848b6efb755 100644 (file)
@@ -108,6 +108,7 @@ fn test_return_in_macro() {
 }
 
 mod issue6501 {
+    #[allow(clippy::unnecessary_lazy_evaluations)]
     fn foo(bar: Result<(), ()>) {
         bar.unwrap_or_else(|_| {})
     }
index c42567b517c9115819b3446d5c5281c0da273884..341caf18bd60c83f73b41ddf81bc99b9f36fd83f 100644 (file)
@@ -108,6 +108,7 @@ fn test_return_in_macro() {
 }
 
 mod issue6501 {
+    #[allow(clippy::unnecessary_lazy_evaluations)]
     fn foo(bar: Result<(), ()>) {
         bar.unwrap_or_else(|_| return)
     }
index 74dda971fdabb632633f4a125ba15d6e2d5ad1a3..c0abc2c63dde1fb33c409476a3ab4ebdaac8823d 100644 (file)
@@ -85,109 +85,109 @@ LL |         return String::new();
    |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:112:32
+  --> $DIR/needless_return.rs:113:32
    |
 LL |         bar.unwrap_or_else(|_| return)
    |                                ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:117:13
+  --> $DIR/needless_return.rs:118:13
    |
 LL |             return;
    |             ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:119:20
+  --> $DIR/needless_return.rs:120:20
    |
 LL |         let _ = || return;
    |                    ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:125:32
+  --> $DIR/needless_return.rs:126:32
    |
 LL |         res.unwrap_or_else(|_| return Foo)
    |                                ^^^^^^^^^^ help: remove `return`: `Foo`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:134:5
+  --> $DIR/needless_return.rs:135:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:138:5
+  --> $DIR/needless_return.rs:139:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:143:9
+  --> $DIR/needless_return.rs:144:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:145:9
+  --> $DIR/needless_return.rs:146:9
    |
 LL |         return false;
    |         ^^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:151:17
+  --> $DIR/needless_return.rs:152:17
    |
 LL |         true => return false,
    |                 ^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:153:13
+  --> $DIR/needless_return.rs:154:13
    |
 LL |             return true;
    |             ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:160:9
+  --> $DIR/needless_return.rs:161:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:162:16
+  --> $DIR/needless_return.rs:163:16
    |
 LL |     let _ = || return true;
    |                ^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:170:5
+  --> $DIR/needless_return.rs:171:5
    |
 LL |     return;
    |     ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:175:9
+  --> $DIR/needless_return.rs:176:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:177:9
+  --> $DIR/needless_return.rs:178:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:184:14
+  --> $DIR/needless_return.rs:185:14
    |
 LL |         _ => return,
    |              ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:199:9
+  --> $DIR/needless_return.rs:200:9
    |
 LL |         return String::from("test");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:201:9
+  --> $DIR/needless_return.rs:202:9
    |
 LL |         return String::new();
    |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
diff --git a/src/tools/clippy/tests/ui/needless_splitn.fixed b/src/tools/clippy/tests/ui/needless_splitn.fixed
new file mode 100644 (file)
index 0000000..f6a4b2f
--- /dev/null
@@ -0,0 +1,27 @@
+// run-rustfix
+// edition:2018
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::needless_splitn)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
+
+extern crate itertools;
+
+#[allow(unused_imports)]
+use itertools::Itertools;
+
+fn main() {
+    let str = "key=value=end";
+    let _ = str.split('=').next();
+    let _ = str.split('=').nth(0);
+    let _ = str.splitn(2, '=').nth(1);
+    let (_, _) = str.splitn(2, '=').next_tuple().unwrap();
+    let (_, _) = str.split('=').next_tuple().unwrap();
+    let _: Vec<&str> = str.splitn(3, '=').collect();
+
+    let _ = str.rsplit('=').next();
+    let _ = str.rsplit('=').nth(0);
+    let _ = str.rsplitn(2, '=').nth(1);
+    let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
+    let (_, _) = str.rsplit('=').next_tuple().unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/needless_splitn.rs b/src/tools/clippy/tests/ui/needless_splitn.rs
new file mode 100644 (file)
index 0000000..6ba3225
--- /dev/null
@@ -0,0 +1,27 @@
+// run-rustfix
+// edition:2018
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::needless_splitn)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
+
+extern crate itertools;
+
+#[allow(unused_imports)]
+use itertools::Itertools;
+
+fn main() {
+    let str = "key=value=end";
+    let _ = str.splitn(2, '=').next();
+    let _ = str.splitn(2, '=').nth(0);
+    let _ = str.splitn(2, '=').nth(1);
+    let (_, _) = str.splitn(2, '=').next_tuple().unwrap();
+    let (_, _) = str.splitn(3, '=').next_tuple().unwrap();
+    let _: Vec<&str> = str.splitn(3, '=').collect();
+
+    let _ = str.rsplitn(2, '=').next();
+    let _ = str.rsplitn(2, '=').nth(0);
+    let _ = str.rsplitn(2, '=').nth(1);
+    let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
+    let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/needless_splitn.stderr b/src/tools/clippy/tests/ui/needless_splitn.stderr
new file mode 100644 (file)
index 0000000..66de225
--- /dev/null
@@ -0,0 +1,40 @@
+error: unnecessary use of `splitn`
+  --> $DIR/needless_splitn.rs:15:13
+   |
+LL |     let _ = str.splitn(2, '=').next();
+   |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+   |
+   = note: `-D clippy::needless-splitn` implied by `-D warnings`
+
+error: unnecessary use of `splitn`
+  --> $DIR/needless_splitn.rs:16:13
+   |
+LL |     let _ = str.splitn(2, '=').nth(0);
+   |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `splitn`
+  --> $DIR/needless_splitn.rs:19:18
+   |
+LL |     let (_, _) = str.splitn(3, '=').next_tuple().unwrap();
+   |                  ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `rsplitn`
+  --> $DIR/needless_splitn.rs:22:13
+   |
+LL |     let _ = str.rsplitn(2, '=').next();
+   |             ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
+
+error: unnecessary use of `rsplitn`
+  --> $DIR/needless_splitn.rs:23:13
+   |
+LL |     let _ = str.rsplitn(2, '=').nth(0);
+   |             ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
+
+error: unnecessary use of `rsplitn`
+  --> $DIR/needless_splitn.rs:26:18
+   |
+LL |     let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
+   |                  ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
+
+error: aborting due to 6 previous errors
+
index 7bcc4cad0d363a78153e36456b0e128eaeef8e45..5427c88faf348e3c3399253280b187f7e42599e0 100644 (file)
@@ -1,4 +1,4 @@
-#![feature(box_syntax)]
+#![feature(box_syntax, fn_traits, unboxed_closures)]
 #![warn(clippy::no_effect_underscore_binding)]
 #![allow(dead_code)]
 #![allow(path_statements)]
@@ -58,6 +58,36 @@ unsafe fn unsafe_fn() -> i32 {
     0
 }
 
+struct GreetStruct1;
+
+impl FnOnce<(&str,)> for GreetStruct1 {
+    type Output = ();
+
+    extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
+        println!("hello {}", who);
+    }
+}
+
+struct GreetStruct2();
+
+impl FnOnce<(&str,)> for GreetStruct2 {
+    type Output = ();
+
+    extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
+        println!("hello {}", who);
+    }
+}
+
+struct GreetStruct3 {}
+
+impl FnOnce<(&str,)> for GreetStruct3 {
+    type Output = ();
+
+    extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
+        println!("hello {}", who);
+    }
+}
+
 fn main() {
     let s = get_struct();
     let s2 = get_struct();
@@ -108,4 +138,7 @@ fn main() {
     DropTuple(0);
     DropEnum::Tuple(0);
     DropEnum::Struct { field: 0 };
+    GreetStruct1("world");
+    GreetStruct2()("world");
+    GreetStruct3 {}("world");
 }
index a5dbc9fef455a4e71028acbfcfa78c4577dda5e5..06b88bb5bee7ab5c3ef908a1a7efc66950c3bcf3 100644 (file)
@@ -1,5 +1,5 @@
 error: statement with no effect
-  --> $DIR/no_effect.rs:65:5
+  --> $DIR/no_effect.rs:95:5
    |
 LL |     0;
    |     ^^
@@ -7,157 +7,157 @@ LL |     0;
    = note: `-D clippy::no-effect` implied by `-D warnings`
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:66:5
+  --> $DIR/no_effect.rs:96:5
    |
 LL |     s2;
    |     ^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:67:5
+  --> $DIR/no_effect.rs:97:5
    |
 LL |     Unit;
    |     ^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:68:5
+  --> $DIR/no_effect.rs:98:5
    |
 LL |     Tuple(0);
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:69:5
+  --> $DIR/no_effect.rs:99:5
    |
 LL |     Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:70:5
+  --> $DIR/no_effect.rs:100:5
    |
 LL |     Struct { ..s };
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:71:5
+  --> $DIR/no_effect.rs:101:5
    |
 LL |     Union { a: 0 };
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:72:5
+  --> $DIR/no_effect.rs:102:5
    |
 LL |     Enum::Tuple(0);
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:73:5
+  --> $DIR/no_effect.rs:103:5
    |
 LL |     Enum::Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:74:5
+  --> $DIR/no_effect.rs:104:5
    |
 LL |     5 + 6;
    |     ^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:75:5
+  --> $DIR/no_effect.rs:105:5
    |
 LL |     *&42;
    |     ^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:76:5
+  --> $DIR/no_effect.rs:106:5
    |
 LL |     &6;
    |     ^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:77:5
+  --> $DIR/no_effect.rs:107:5
    |
 LL |     (5, 6, 7);
    |     ^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:78:5
+  --> $DIR/no_effect.rs:108:5
    |
 LL |     box 42;
    |     ^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:79:5
+  --> $DIR/no_effect.rs:109:5
    |
 LL |     ..;
    |     ^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:80:5
+  --> $DIR/no_effect.rs:110:5
    |
 LL |     5..;
    |     ^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:81:5
+  --> $DIR/no_effect.rs:111:5
    |
 LL |     ..5;
    |     ^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:82:5
+  --> $DIR/no_effect.rs:112:5
    |
 LL |     5..6;
    |     ^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:83:5
+  --> $DIR/no_effect.rs:113:5
    |
 LL |     5..=6;
    |     ^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:84:5
+  --> $DIR/no_effect.rs:114:5
    |
 LL |     [42, 55];
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:85:5
+  --> $DIR/no_effect.rs:115:5
    |
 LL |     [42, 55][1];
    |     ^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:86:5
+  --> $DIR/no_effect.rs:116:5
    |
 LL |     (42, 55).1;
    |     ^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:87:5
+  --> $DIR/no_effect.rs:117:5
    |
 LL |     [42; 55];
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:88:5
+  --> $DIR/no_effect.rs:118:5
    |
 LL |     [42; 55][13];
    |     ^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:90:5
+  --> $DIR/no_effect.rs:120:5
    |
 LL |     || x += 5;
    |     ^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:92:5
+  --> $DIR/no_effect.rs:122:5
    |
 LL |     FooString { s: s };
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:93:5
+  --> $DIR/no_effect.rs:123:5
    |
 LL |     let _unused = 1;
    |     ^^^^^^^^^^^^^^^^
@@ -165,19 +165,19 @@ LL |     let _unused = 1;
    = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings`
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:94:5
+  --> $DIR/no_effect.rs:124:5
    |
 LL |     let _penguin = || println!("Some helpful closure");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:95:5
+  --> $DIR/no_effect.rs:125:5
    |
 LL |     let _duck = Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:96:5
+  --> $DIR/no_effect.rs:126:5
    |
 LL |     let _cat = [2, 4, 6, 8][2];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
index eca7f5e5655913785f3e98cf94d3a179549e74ba..828248d922f890837bd239447d494039eecaff3a 100644 (file)
@@ -69,6 +69,11 @@ pub enum MyOption<T> {
 
 unsafe impl<T> Send for MyOption<T> {}
 
+// Test types that contain `NonNull` instead of raw pointers (#8045)
+pub struct WrappedNonNull(UnsafeCell<NonNull<()>>);
+
+unsafe impl Send for WrappedNonNull {}
+
 // Multiple type parameters
 pub struct MultiParam<A, B> {
     vec: Vec<(A, B)>,
index 8b8a1d16d9bb96172bd747f011814853230725b9..3c4da36b3e054d04152c285542c622b45013254f 100644 (file)
@@ -103,65 +103,65 @@ LL |     MySome(T),
    = help: add `T: Send` bound in `Send` impl
 
 error: this implementation is unsound, as some fields in `MultiParam<A, B>` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:77:1
+  --> $DIR/non_send_fields_in_send_ty.rs:82:1
    |
 LL | unsafe impl<A, B> Send for MultiParam<A, B> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the type of field `vec` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:74:5
+  --> $DIR/non_send_fields_in_send_ty.rs:79:5
    |
 LL |     vec: Vec<(A, B)>,
    |     ^^^^^^^^^^^^^^^^
    = help: add bounds on type parameters `A, B` that satisfy `Vec<(A, B)>: Send`
 
 error: this implementation is unsound, as some fields in `HeuristicTest` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:95:1
+  --> $DIR/non_send_fields_in_send_ty.rs:100:1
    |
 LL | unsafe impl Send for HeuristicTest {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the type of field `field4` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:90:5
+  --> $DIR/non_send_fields_in_send_ty.rs:95:5
    |
 LL |     field4: (*const NonSend, Rc<u8>),
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: use a thread-safe type that implements `Send`
 
 error: this implementation is unsound, as some fields in `AttrTest3<T>` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:114:1
+  --> $DIR/non_send_fields_in_send_ty.rs:119:1
    |
 LL | unsafe impl<T> Send for AttrTest3<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the type of field `0` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:109:11
+  --> $DIR/non_send_fields_in_send_ty.rs:114:11
    |
 LL |     Enum2(T),
    |           ^
    = help: add `T: Send` bound in `Send` impl
 
 error: this implementation is unsound, as some fields in `Complex<P, u32>` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:122:1
+  --> $DIR/non_send_fields_in_send_ty.rs:127:1
    |
 LL | unsafe impl<P> Send for Complex<P, u32> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the type of field `field1` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:118:5
+  --> $DIR/non_send_fields_in_send_ty.rs:123:5
    |
 LL |     field1: A,
    |     ^^^^^^^^^
    = help: add `P: Send` bound in `Send` impl
 
 error: this implementation is unsound, as some fields in `Complex<Q, MutexGuard<'static, bool>>` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:125:1
+  --> $DIR/non_send_fields_in_send_ty.rs:130:1
    |
 LL | unsafe impl<Q: Send> Send for Complex<Q, MutexGuard<'static, bool>> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the type of field `field2` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:119:5
+  --> $DIR/non_send_fields_in_send_ty.rs:124:5
    |
 LL |     field2: B,
    |     ^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/octal_escapes.rs b/src/tools/clippy/tests/ui/octal_escapes.rs
new file mode 100644 (file)
index 0000000..53145ef
--- /dev/null
@@ -0,0 +1,20 @@
+#![warn(clippy::octal_escapes)]
+
+fn main() {
+    let _bad1 = "\033[0m";
+    let _bad2 = b"\033[0m";
+    let _bad3 = "\\\033[0m";
+    // maximum 3 digits (\012 is the escape)
+    let _bad4 = "\01234567";
+    let _bad5 = "\0\03";
+    let _bad6 = "Text-\055\077-MoreText";
+    let _bad7 = "EvenMoreText-\01\02-ShortEscapes";
+    let _bad8 = "锈\01锈";
+    let _bad9 = "锈\011锈";
+
+    let _good1 = "\\033[0m";
+    let _good2 = "\0\\0";
+    let _good3 = "\0\0";
+    let _good4 = "X\0\0X";
+    let _good5 = "锈\0锈";
+}
diff --git a/src/tools/clippy/tests/ui/octal_escapes.stderr b/src/tools/clippy/tests/ui/octal_escapes.stderr
new file mode 100644 (file)
index 0000000..54f5bbb
--- /dev/null
@@ -0,0 +1,131 @@
+error: octal-looking escape in string literal
+  --> $DIR/octal_escapes.rs:4:17
+   |
+LL |     let _bad1 = "/033[0m";
+   |                 ^^^^^^^^^
+   |
+   = note: `-D clippy::octal-escapes` implied by `-D warnings`
+   = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad1 = "/x1b[0m";
+   |                 ~~~~~~~~~
+help: if the null character is intended, disambiguate using
+   |
+LL |     let _bad1 = "/x0033[0m";
+   |                 ~~~~~~~~~~~
+
+error: octal-looking escape in byte string literal
+  --> $DIR/octal_escapes.rs:5:17
+   |
+LL |     let _bad2 = b"/033[0m";
+   |                 ^^^^^^^^^^
+   |
+   = help: octal escapes are not supported, `/0` is always a null byte
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad2 = b"/x1b[0m";
+   |                 ~~~~~~~~~~
+help: if the null byte is intended, disambiguate using
+   |
+LL |     let _bad2 = b"/x0033[0m";
+   |                 ~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+  --> $DIR/octal_escapes.rs:6:17
+   |
+LL |     let _bad3 = "//033[0m";
+   |                 ^^^^^^^^^^^
+   |
+   = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad3 = "//x1b[0m";
+   |                 ~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+   |
+LL |     let _bad3 = "//x0033[0m";
+   |                 ~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+  --> $DIR/octal_escapes.rs:8:17
+   |
+LL |     let _bad4 = "/01234567";
+   |                 ^^^^^^^^^^^
+   |
+   = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad4 = "/x0a34567";
+   |                 ~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+   |
+LL |     let _bad4 = "/x001234567";
+   |                 ~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+  --> $DIR/octal_escapes.rs:10:17
+   |
+LL |     let _bad6 = "Text-/055/077-MoreText";
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad6 = "Text-/x2d/x3f-MoreText";
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+   |
+LL |     let _bad6 = "Text-/x0055/x0077-MoreText";
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+  --> $DIR/octal_escapes.rs:11:17
+   |
+LL |     let _bad7 = "EvenMoreText-/01/02-ShortEscapes";
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad7 = "EvenMoreText-/x01/x02-ShortEscapes";
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+   |
+LL |     let _bad7 = "EvenMoreText-/x001/x002-ShortEscapes";
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+  --> $DIR/octal_escapes.rs:12:17
+   |
+LL |     let _bad8 = "锈/01锈";
+   |                 ^^^^^^^^^
+   |
+   = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad8 = "锈/x01锈";
+   |                 ~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+   |
+LL |     let _bad8 = "锈/x001锈";
+   |                 ~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+  --> $DIR/octal_escapes.rs:13:17
+   |
+LL |     let _bad9 = "锈/011锈";
+   |                 ^^^^^^^^^^
+   |
+   = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad9 = "锈/x09锈";
+   |                 ~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+   |
+LL |     let _bad9 = "锈/x0011锈";
+   |                 ~~~~~~~~~~~~
+
+error: aborting due to 8 previous errors
+
index 642c77460a3407076ed9be98fa17eeb94feb1196..0141fb7856d06abf4a27857b45e9a86f756ca4f3 100644 (file)
@@ -1,5 +1,6 @@
 // aux-build:macro_rules.rs
 #![warn(clippy::option_env_unwrap)]
+#![allow(clippy::map_flatten)]
 
 #[macro_use]
 extern crate macro_rules;
index e6a58b0b2b752f3cca87b1a42816c2900a37953b..885ac096cc8f763088d577bdcd7cfed630d71bc0 100644 (file)
@@ -1,5 +1,5 @@
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:17:13
+  --> $DIR/option_env_unwrap.rs:18:13
    |
 LL |     let _ = option_env!("PATH").unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     let _ = option_env!("PATH").unwrap();
    = help: consider using the `env!` macro instead
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:18:13
+  --> $DIR/option_env_unwrap.rs:19:13
    |
 LL |     let _ = option_env!("PATH").expect("environment variable PATH isn't set");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL |     let _ = option_env!("PATH").expect("environment variable PATH isn't set
    = help: consider using the `env!` macro instead
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:9:9
+  --> $DIR/option_env_unwrap.rs:10:9
    |
 LL |         option_env!($env).unwrap()
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -28,7 +28,7 @@ LL |     let _ = option_env_unwrap!("PATH");
    = note: this error originates in the macro `option_env_unwrap` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:12:9
+  --> $DIR/option_env_unwrap.rs:13:9
    |
 LL |         option_env!($env).expect($message)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL |     let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set
    = note: this error originates in the macro `option_env_unwrap` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:21:13
+  --> $DIR/option_env_unwrap.rs:22:13
    |
 LL |     let _ = option_env_unwrap_external!("PATH");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL |     let _ = option_env_unwrap_external!("PATH");
    = note: this error originates in the macro `option_env_unwrap_external` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:22:13
+  --> $DIR/option_env_unwrap.rs:23:13
    |
 LL |     let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index f9d1825ade05483c44351a1527efcbf77cdf786a..b20f73f3110dee44367854c86225c6de50f81372 100644 (file)
@@ -1,8 +1,6 @@
-#![warn(clippy::option_filter_map)]
 // run-rustfix
-fn odds_out(x: i32) -> Option<i32> {
-    if x % 2 == 0 { Some(x) } else { None }
-}
+#![warn(clippy::option_filter_map)]
+#![allow(clippy::map_flatten)]
 
 fn main() {
     let _ = Some(Some(1)).flatten();
@@ -21,3 +19,7 @@ fn main() {
         .map(odds_out)
         .flatten();
 }
+
+fn odds_out(x: i32) -> Option<i32> {
+    if x % 2 == 0 { Some(x) } else { None }
+}
index 588e1ccccce20149fc5750b4c9c57575cf846f7d..7abaaa0fb83b35883da6233109796da4ba708502 100644 (file)
@@ -1,8 +1,6 @@
-#![warn(clippy::option_filter_map)]
 // run-rustfix
-fn odds_out(x: i32) -> Option<i32> {
-    if x % 2 == 0 { Some(x) } else { None }
-}
+#![warn(clippy::option_filter_map)]
+#![allow(clippy::map_flatten)]
 
 fn main() {
     let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap);
@@ -23,3 +21,7 @@ fn main() {
         .filter(|o| o.is_some())
         .map(|o| o.unwrap());
 }
+
+fn odds_out(x: i32) -> Option<i32> {
+    if x % 2 == 0 { Some(x) } else { None }
+}
index 31a82969d5a1c9bd0a9e4716a14b2f98e1c9a2a7..4a030ac9ab045440cbc69b85688cb3d0159d1ccb 100644 (file)
@@ -1,5 +1,5 @@
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:8:27
+  --> $DIR/option_filter_map.rs:6:27
    |
 LL |     let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap);
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
@@ -7,37 +7,37 @@ LL |     let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap);
    = note: `-D clippy::option-filter-map` implied by `-D warnings`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:9:27
+  --> $DIR/option_filter_map.rs:7:27
    |
 LL |     let _ = Some(Some(1)).filter(|o| o.is_some()).map(|o| o.unwrap());
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:10:35
+  --> $DIR/option_filter_map.rs:8:35
    |
 LL |     let _ = Some(1).map(odds_out).filter(Option::is_some).map(Option::unwrap);
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:11:35
+  --> $DIR/option_filter_map.rs:9:35
    |
 LL |     let _ = Some(1).map(odds_out).filter(|o| o.is_some()).map(|o| o.unwrap());
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:13:39
+  --> $DIR/option_filter_map.rs:11:39
    |
 LL |     let _ = vec![Some(1)].into_iter().filter(Option::is_some).map(Option::unwrap);
    |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:14:39
+  --> $DIR/option_filter_map.rs:12:39
    |
 LL |     let _ = vec![Some(1)].into_iter().filter(|o| o.is_some()).map(|o| o.unwrap());
    |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:18:10
+  --> $DIR/option_filter_map.rs:16:10
    |
 LL |           .filter(Option::is_some)
    |  __________^
@@ -45,7 +45,7 @@ LL | |         .map(Option::unwrap);
    | |____________________________^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:23:10
+  --> $DIR/option_filter_map.rs:21:10
    |
 LL |           .filter(|o| o.is_some())
    |  __________^
index 9cb6a9d1ecc9bc9e91c0cf071c748ab45a962711..ce3093c542ae0b7bfad86c1c10e665db25761cfa 100644 (file)
@@ -75,6 +75,17 @@ fn negative_tests(arg: Option<u32>) -> u32 {
     7
 }
 
+// #7973
+fn pattern_to_vec(pattern: &str) -> Vec<String> {
+    pattern
+        .trim_matches('/')
+        .split('/')
+        .flat_map(|s| {
+            s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])
+        })
+        .collect::<Vec<_>>()
+}
+
 fn main() {
     let optional = Some(5);
     let _ = optional.map_or(5, |x| x + 2);
@@ -110,7 +121,7 @@ fn main() {
 
     let s = String::new();
     // Lint, both branches immutably borrow `s`.
-    let _ = Some(0).map_or_else(|| s.len(), |x| s.len() + x);
+    let _ = Some(0).map_or(s.len(), |x| s.len() + x);
 
     let s = String::new();
     // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
@@ -146,4 +157,6 @@ fn main() {
         // Don't lint. `await` can't be moved into a closure.
         let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
     }
+
+    let _ = pattern_to_vec("hello world");
 }
index b3ba5eb870a693e65053cf50bbc1c914520fbe4a..c228b2f43d10c41420440776d50a9f6c9c47d945 100644 (file)
@@ -94,6 +94,21 @@ fn negative_tests(arg: Option<u32>) -> u32 {
     7
 }
 
+// #7973
+fn pattern_to_vec(pattern: &str) -> Vec<String> {
+    pattern
+        .trim_matches('/')
+        .split('/')
+        .flat_map(|s| {
+            if let Some(idx) = s.find('.') {
+                vec![s[..idx].to_string(), s[idx..].to_string()]
+            } else {
+                vec![s.to_string()]
+            }
+        })
+        .collect::<Vec<_>>()
+}
+
 fn main() {
     let optional = Some(5);
     let _ = if let Some(x) = optional { x + 2 } else { 5 };
@@ -171,4 +186,6 @@ async fn _f2() {
         // Don't lint. `await` can't be moved into a closure.
         let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
     }
+
+    let _ = pattern_to_vec("hello world");
 }
index 685bb48ea37bc05baf1945d6a66b7c12f69c0934..4e64cd7cdb1d649a73edfaedf1a97bce0ac0b742 100644 (file)
@@ -142,14 +142,24 @@ LL +         y
 LL ~     }, |x| x * x * x * x);
    |
 
+error: use Option::map_or_else instead of an if let/else
+  --> $DIR/option_if_let_else.rs:103:13
+   |
+LL | /             if let Some(idx) = s.find('.') {
+LL | |                 vec![s[..idx].to_string(), s[idx..].to_string()]
+LL | |             } else {
+LL | |                 vec![s.to_string()]
+LL | |             }
+   | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])`
+
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:99:13
+  --> $DIR/option_if_let_else.rs:114:13
    |
 LL |     let _ = if let Some(x) = optional { x + 2 } else { 5 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:108:13
+  --> $DIR/option_if_let_else.rs:123:13
    |
 LL |       let _ = if let Some(x) = Some(0) {
    |  _____________^
@@ -170,14 +180,14 @@ LL +             }
 LL ~         });
    |
 
-error: use Option::map_or_else instead of an if let/else
-  --> $DIR/option_if_let_else.rs:136:13
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:151:13
    |
 LL |     let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or_else(|| s.len(), |x| s.len() + x)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:140:13
+  --> $DIR/option_if_let_else.rs:155:13
    |
 LL |       let _ = if let Some(x) = Some(0) {
    |  _____________^
@@ -196,5 +206,5 @@ LL +         s.len() + x
 LL ~     });
    |
 
-error: aborting due to 14 previous errors
+error: aborting due to 15 previous errors
 
index d80c3c7c1b7225664a91e95254005e748d919c9a..04bfac7735fa617a1f62fe3b91b959be016d8548 100644 (file)
@@ -4,13 +4,23 @@
 
 fn main() {
     let opt = Some(1);
+    let r: Result<i32, i32> = Ok(1);
+    let bar = |_| Some(1);
 
     // Check `OPTION_MAP_OR_NONE`.
     // Single line case.
-    let _ = opt.and_then(|x| Some(x + 1));
+    let _: Option<i32> = opt.map(|x| x + 1);
     // Multi-line case.
     #[rustfmt::skip]
-    let _ = opt.and_then(|x| {
-                        Some(x + 1)
-                       });
+    let _: Option<i32> = opt.map(|x| x + 1);
+    // function returning `Option`
+    let _: Option<i32> = opt.and_then(bar);
+    let _: Option<i32> = opt.and_then(|x| {
+        let offset = 0;
+        let height = x;
+        Some(offset + height)
+    });
+
+    // Check `RESULT_MAP_OR_INTO_OPTION`.
+    let _: Option<i32> = r.ok();
 }
index 629842419e546d1af1e57ffe1c8062797a46f04d..bb84f8a48f45d9c2a0710113d6fbe4e3ee6b9426 100644 (file)
@@ -4,13 +4,25 @@
 
 fn main() {
     let opt = Some(1);
+    let r: Result<i32, i32> = Ok(1);
+    let bar = |_| Some(1);
 
     // Check `OPTION_MAP_OR_NONE`.
     // Single line case.
-    let _ = opt.map_or(None, |x| Some(x + 1));
+    let _: Option<i32> = opt.map_or(None, |x| Some(x + 1));
     // Multi-line case.
     #[rustfmt::skip]
-    let _ = opt.map_or(None, |x| {
+    let _: Option<i32> = opt.map_or(None, |x| {
                         Some(x + 1)
                        });
+    // function returning `Option`
+    let _: Option<i32> = opt.map_or(None, bar);
+    let _: Option<i32> = opt.map_or(None, |x| {
+        let offset = 0;
+        let height = x;
+        Some(offset + height)
+    });
+
+    // Check `RESULT_MAP_OR_INTO_OPTION`.
+    let _: Option<i32> = r.map_or(None, Some);
 }
index 27d68b85e6f81f853d59e6aa613e8c39904696ca..7befcb890863a7caf4b6412357a71fb90f49776b 100644 (file)
@@ -1,26 +1,53 @@
-error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
-  --> $DIR/option_map_or_none.rs:10:13
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `map(..)` instead
+  --> $DIR/option_map_or_none.rs:12:26
    |
-LL |     let _ = opt.map_or(None, |x| Some(x + 1));
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `opt.and_then(|x| Some(x + 1))`
+LL |     let _: Option<i32> = opt.map_or(None, |x| Some(x + 1));
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `map` instead: `opt.map(|x| x + 1)`
    |
    = note: `-D clippy::option-map-or-none` implied by `-D warnings`
 
-error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
-  --> $DIR/option_map_or_none.rs:13:13
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `map(..)` instead
+  --> $DIR/option_map_or_none.rs:15:26
    |
-LL |       let _ = opt.map_or(None, |x| {
-   |  _____________^
+LL |       let _: Option<i32> = opt.map_or(None, |x| {
+   |  __________________________^
 LL | |                         Some(x + 1)
 LL | |                        });
-   | |_________________________^
+   | |_________________________^ help: try using `map` instead: `opt.map(|x| x + 1)`
+
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
+  --> $DIR/option_map_or_none.rs:19:26
+   |
+LL |     let _: Option<i32> = opt.map_or(None, bar);
+   |                          ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `opt.and_then(bar)`
+
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
+  --> $DIR/option_map_or_none.rs:20:26
+   |
+LL |       let _: Option<i32> = opt.map_or(None, |x| {
+   |  __________________________^
+LL | |         let offset = 0;
+LL | |         let height = x;
+LL | |         Some(offset + height)
+LL | |     });
+   | |______^
    |
 help: try using `and_then` instead
    |
-LL ~     let _ = opt.and_then(|x| {
-LL +                         Some(x + 1)
-LL ~                        });
+LL ~     let _: Option<i32> = opt.and_then(|x| {
+LL +         let offset = 0;
+LL +         let height = x;
+LL +         Some(offset + height)
+LL ~     });
+   |
+
+error: called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling `ok()` instead
+  --> $DIR/option_map_or_none.rs:27:26
+   |
+LL |     let _: Option<i32> = r.map_or(None, Some);
+   |                          ^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `r.ok()`
    |
+   = note: `-D clippy::result-map-or-into-option` implied by `-D warnings`
 
-error: aborting due to 2 previous errors
+error: aborting due to 5 previous errors
 
index c2f94d0e8575650272656acba755013a0de37ddb..d6d6ab49734e782ba7887824e86d41a8f29bcc22 100644 (file)
@@ -43,7 +43,7 @@ fn or_fun_call() {
     with_enum.unwrap_or(Enum::A(5));
 
     let with_const_fn = Some(Duration::from_secs(1));
-    with_const_fn.unwrap_or_else(|| Duration::from_secs(5));
+    with_const_fn.unwrap_or(Duration::from_secs(5));
 
     let with_constructor = Some(vec![1]);
     with_constructor.unwrap_or_else(make);
@@ -79,16 +79,16 @@ fn or_fun_call() {
     without_default.unwrap_or_else(Foo::new);
 
     let mut map = HashMap::<u64, String>::new();
-    map.entry(42).or_insert_with(String::new);
+    map.entry(42).or_insert(String::new());
 
     let mut map_vec = HashMap::<u64, Vec<i32>>::new();
-    map_vec.entry(42).or_insert_with(Vec::new);
+    map_vec.entry(42).or_insert(vec![]);
 
     let mut btree = BTreeMap::<u64, String>::new();
-    btree.entry(42).or_insert_with(String::new);
+    btree.entry(42).or_insert(String::new());
 
     let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
-    btree_vec.entry(42).or_insert_with(Vec::new);
+    btree_vec.entry(42).or_insert(vec![]);
 
     let stringy = Some(String::from(""));
     let _ = stringy.unwrap_or_else(|| "".to_owned());
@@ -129,7 +129,7 @@ fn test_or_with_ctors() {
 
     let b = "b".to_string();
     let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
-        .or_else(|| Some(Bar(b, Duration::from_secs(2))));
+        .or(Some(Bar(b, Duration::from_secs(2))));
 
     let vec = vec!["foo"];
     let _ = opt.ok_or(vec.len());
@@ -155,16 +155,24 @@ fn f() -> Option<()> {
 }
 
 mod issue6675 {
+    unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T {
+        #[allow(unused)]
+        let x = vec![0; 1000]; // future-proofing, make this function expensive.
+        &*p
+    }
+
     unsafe fn foo() {
-        let mut s = "test".to_owned();
-        None.unwrap_or_else(|| s.as_mut_vec());
+        let s = "test".to_owned();
+        let s = &s as *const _;
+        None.unwrap_or_else(|| ptr_to_ref(s));
     }
 
     fn bar() {
-        let mut s = "test".to_owned();
-        None.unwrap_or_else(|| unsafe { s.as_mut_vec() });
+        let s = "test".to_owned();
+        let s = &s as *const _;
+        None.unwrap_or_else(|| unsafe { ptr_to_ref(s) });
         #[rustfmt::skip]
-        None.unwrap_or_else(|| unsafe { s.as_mut_vec() });
+        None.unwrap_or_else(|| unsafe { ptr_to_ref(s) });
     }
 }
 
index afaf92961b0274f75d9e5ffbbd2f40b606fb876a..8eadc6ce3b47acace2c3fcf4cea29fd2db831b92 100644 (file)
@@ -155,16 +155,24 @@ fn f() -> Option<()> {
 }
 
 mod issue6675 {
+    unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T {
+        #[allow(unused)]
+        let x = vec![0; 1000]; // future-proofing, make this function expensive.
+        &*p
+    }
+
     unsafe fn foo() {
-        let mut s = "test".to_owned();
-        None.unwrap_or(s.as_mut_vec());
+        let s = "test".to_owned();
+        let s = &s as *const _;
+        None.unwrap_or(ptr_to_ref(s));
     }
 
     fn bar() {
-        let mut s = "test".to_owned();
-        None.unwrap_or(unsafe { s.as_mut_vec() });
+        let s = "test".to_owned();
+        let s = &s as *const _;
+        None.unwrap_or(unsafe { ptr_to_ref(s) });
         #[rustfmt::skip]
-        None.unwrap_or( unsafe { s.as_mut_vec() }    );
+        None.unwrap_or( unsafe { ptr_to_ref(s) }    );
     }
 }
 
index b2bcbd38c2df36061eefebf4f788821471955d6f..9d0c42b10c27f500eaa0469e43d1d1bcbfb0ae65 100644 (file)
@@ -1,16 +1,10 @@
-error: use of `unwrap_or` followed by a function call
-  --> $DIR/or_fun_call.rs:46:19
-   |
-LL |     with_const_fn.unwrap_or(Duration::from_secs(5));
-   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Duration::from_secs(5))`
-   |
-   = note: `-D clippy::or-fun-call` implied by `-D warnings`
-
 error: use of `unwrap_or` followed by a function call
   --> $DIR/or_fun_call.rs:49:22
    |
 LL |     with_constructor.unwrap_or(make());
    |                      ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)`
+   |
+   = note: `-D clippy::or-fun-call` implied by `-D warnings`
 
 error: use of `unwrap_or` followed by a call to `new`
   --> $DIR/or_fun_call.rs:52:5
@@ -72,30 +66,6 @@ error: use of `unwrap_or` followed by a function call
 LL |     without_default.unwrap_or(Foo::new());
    |                     ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
 
-error: use of `or_insert` followed by a function call
-  --> $DIR/or_fun_call.rs:82:19
-   |
-LL |     map.entry(42).or_insert(String::new());
-   |                   ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)`
-
-error: use of `or_insert` followed by a function call
-  --> $DIR/or_fun_call.rs:85:23
-   |
-LL |     map_vec.entry(42).or_insert(vec![]);
-   |                       ^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(Vec::new)`
-
-error: use of `or_insert` followed by a function call
-  --> $DIR/or_fun_call.rs:88:21
-   |
-LL |     btree.entry(42).or_insert(String::new());
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)`
-
-error: use of `or_insert` followed by a function call
-  --> $DIR/or_fun_call.rs:91:25
-   |
-LL |     btree_vec.entry(42).or_insert(vec![]);
-   |                         ^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(Vec::new)`
-
 error: use of `unwrap_or` followed by a function call
   --> $DIR/or_fun_call.rs:94:21
    |
@@ -120,29 +90,23 @@ error: use of `or` followed by a function call
 LL |     let _ = Some("a".to_string()).or(Some("b".to_string()));
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))`
 
-error: use of `or` followed by a function call
-  --> $DIR/or_fun_call.rs:132:10
-   |
-LL |         .or(Some(Bar(b, Duration::from_secs(2))));
-   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))`
-
 error: use of `unwrap_or` followed by a function call
-  --> $DIR/or_fun_call.rs:160:14
+  --> $DIR/or_fun_call.rs:167:14
    |
-LL |         None.unwrap_or(s.as_mut_vec());
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| s.as_mut_vec())`
+LL |         None.unwrap_or(ptr_to_ref(s));
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| ptr_to_ref(s))`
 
 error: use of `unwrap_or` followed by a function call
-  --> $DIR/or_fun_call.rs:165:14
+  --> $DIR/or_fun_call.rs:173:14
    |
-LL |         None.unwrap_or(unsafe { s.as_mut_vec() });
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })`
+LL |         None.unwrap_or(unsafe { ptr_to_ref(s) });
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 
 error: use of `unwrap_or` followed by a function call
-  --> $DIR/or_fun_call.rs:167:14
+  --> $DIR/or_fun_call.rs:175:14
    |
-LL |         None.unwrap_or( unsafe { s.as_mut_vec() }    );
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })`
+LL |         None.unwrap_or( unsafe { ptr_to_ref(s) }    );
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 
-error: aborting due to 24 previous errors
+error: aborting due to 18 previous errors
 
index 9b4f2f1f57934dbd8332c2932bfed5f30836626a..55a8c26215e9e3356542133f08af2986df90b980 100644 (file)
@@ -37,4 +37,13 @@ fn should_not_lint() {
         Some(_) => (),
         _ => (),
     }
+
+    const FOO: &str = "foo";
+
+    fn foo(s: &str) -> i32 {
+        match s {
+            FOO => 1,
+            _ => 0,
+        }
+    }
 }
index 1f4864b72895bf10142574e214bdfe352eba896a..5612827bd39331f5804d5866c8c534ceab0d1436 100644 (file)
@@ -1,6 +1,7 @@
 // non rustfixable, see redundant_closure_call_fixable.rs
 
 #![warn(clippy::redundant_closure_call)]
+#![allow(clippy::needless_late_init)]
 
 fn main() {
     let mut i = 1;
index 7c8865f1bd375e02a14c5ab99fadd78c88c0f83e..4eca43a2b599ab149a41f67dbd2f7ab875b277d4 100644 (file)
@@ -1,5 +1,5 @@
 error: closure called just once immediately after it was declared
-  --> $DIR/redundant_closure_call_late.rs:15:5
+  --> $DIR/redundant_closure_call_late.rs:16:5
    |
 LL |     i = redun_closure();
    |     ^^^^^^^^^^^^^^^^^^^
@@ -7,13 +7,13 @@ LL |     i = redun_closure();
    = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
 
 error: closure called just once immediately after it was declared
-  --> $DIR/redundant_closure_call_late.rs:19:5
+  --> $DIR/redundant_closure_call_late.rs:20:5
    |
 LL |     i = shadowed_closure();
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 error: closure called just once immediately after it was declared
-  --> $DIR/redundant_closure_call_late.rs:21:5
+  --> $DIR/redundant_closure_call_late.rs:22:5
    |
 LL |     i = shadowed_closure();
    |     ^^^^^^^^^^^^^^^^^^^^^^
index e8a6e940c01cdbebd76dfcceb066bafea11d97cc..64f566735cd95c366eb04b65ffdbe563ea57ee1e 100644 (file)
@@ -1,5 +1,5 @@
 #![warn(clippy::redundant_else)]
-#![allow(clippy::needless_return, clippy::if_same_then_else)]
+#![allow(clippy::needless_return, clippy::if_same_then_else, clippy::needless_late_init)]
 
 fn main() {
     loop {
index 813e268a60c3debe1b49c4a4b4e81bad6f5b4f7d..cc93859269c228c990bb7707bf711f6f0461572f 100644 (file)
@@ -80,3 +80,9 @@ const fn issue6067() {
 
     None::<()>.is_none();
 }
+
+#[allow(clippy::deref_addrof, dead_code)]
+fn issue7921() {
+    if (&None::<()>).is_none() {}
+    if (&None::<()>).is_none() {}
+}
index 82a98468943dfcffc67a064be39a1a3dd4ba6f6f..280dca40c011d18bda8fa4b18e32bb7c0ede0f6b 100644 (file)
@@ -95,3 +95,9 @@ const fn issue6067() {
         None => true,
     };
 }
+
+#[allow(clippy::deref_addrof, dead_code)]
+fn issue7921() {
+    if let None = *(&None::<()>) {}
+    if let None = *&None::<()> {}
+}
index 3a58e5ad7bee96ff94202e42dc5cf99ec17928fb..27ff812ba45ec0b9245dd00e8e27336a6150efd6 100644 (file)
@@ -130,5 +130,17 @@ LL | |         None => true,
 LL | |     };
    | |_____^ help: try this: `None::<()>.is_none()`
 
-error: aborting due to 19 previous errors
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_option.rs:101:12
+   |
+LL |     if let None = *(&None::<()>) {}
+   |     -------^^^^----------------- help: try this: `if (&None::<()>).is_none()`
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_option.rs:102:12
+   |
+LL |     if let None = *&None::<()> {}
+   |     -------^^^^--------------- help: try this: `if (&None::<()>).is_none()`
+
+error: aborting due to 21 previous errors
 
index d7af5d762ae4a9f8485feba9e4ae915c95f76100..83c783385efec866d9dcf0df9503e96c4a7c4c2f 100644 (file)
@@ -70,8 +70,8 @@ fn issue5504() {
     }
 
     fn try_result_opt() -> Result<i32, i32> {
-        while r#try!(result_opt()).is_some() {}
-        if r#try!(result_opt()).is_some() {}
+        while (r#try!(result_opt())).is_some() {}
+        if (r#try!(result_opt())).is_some() {}
         Ok(42)
     }
 
index e06f095da20c6a723f19ef019ca64de33684072b..d674d061e4dd701e82f06219b01f44c65f204e8a 100644 (file)
@@ -88,13 +88,13 @@ error: redundant pattern matching, consider using `is_some()`
   --> $DIR/redundant_pattern_matching_result.rs:85:19
    |
 LL |         while let Some(_) = r#try!(result_opt()) {}
-   |         ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
+   |         ----------^^^^^^^----------------------- help: try this: `while (r#try!(result_opt())).is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
   --> $DIR/redundant_pattern_matching_result.rs:86:16
    |
 LL |         if let Some(_) = r#try!(result_opt()) {}
-   |         -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
+   |         -------^^^^^^^----------------------- help: try this: `if (r#try!(result_opt())).is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
   --> $DIR/redundant_pattern_matching_result.rs:92:12
index cc295b509bc51973cd1bf3f03d591bee3bc732e3..b9425733a8b27b8d1806b7ec921dee3d9936ca11 100644 (file)
@@ -6,6 +6,7 @@
 #![allow(clippy::module_name_repetitions)]
 #![allow(clippy::new_without_default)]
 #![allow(clippy::redundant_static_lifetimes)]
+#![allow(clippy::cognitive_complexity)]
 #![allow(clippy::bind_instead_of_map)]
 #![allow(clippy::box_collection)]
 #![allow(clippy::blocks_in_if_conditions)]
@@ -17,6 +18,8 @@
 #![allow(clippy::invisible_characters)]
 #![allow(clippy::single_char_add_str)]
 #![allow(clippy::match_result_ok)]
+#![allow(clippy::disallowed_types)]
+#![allow(clippy::disallowed_methods)]
 // uplifted lints
 #![allow(invalid_value)]
 #![allow(array_into_iter)]
@@ -49,6 +52,8 @@
 #![warn(clippy::invisible_characters)]
 #![warn(clippy::single_char_add_str)]
 #![warn(clippy::match_result_ok)]
+#![warn(clippy::disallowed_types)]
+#![warn(clippy::disallowed_methods)]
 // uplifted lints
 #![warn(invalid_value)]
 #![warn(array_into_iter)]
index 377075c02464a85becebae4817f176745f490ea0..341c003b9df30548c02e2f07911746cb139089af 100644 (file)
@@ -6,6 +6,7 @@
 #![allow(clippy::module_name_repetitions)]
 #![allow(clippy::new_without_default)]
 #![allow(clippy::redundant_static_lifetimes)]
+#![allow(clippy::cognitive_complexity)]
 #![allow(clippy::bind_instead_of_map)]
 #![allow(clippy::box_collection)]
 #![allow(clippy::blocks_in_if_conditions)]
@@ -17,6 +18,8 @@
 #![allow(clippy::invisible_characters)]
 #![allow(clippy::single_char_add_str)]
 #![allow(clippy::match_result_ok)]
+#![allow(clippy::disallowed_types)]
+#![allow(clippy::disallowed_methods)]
 // uplifted lints
 #![allow(invalid_value)]
 #![allow(array_into_iter)]
@@ -49,6 +52,8 @@
 #![warn(clippy::zero_width_space)]
 #![warn(clippy::single_char_push_str)]
 #![warn(clippy::if_let_some_result)]
+#![warn(clippy::disallowed_type)]
+#![warn(clippy::disallowed_method)]
 // uplifted lints
 #![warn(clippy::invalid_ref)]
 #![warn(clippy::into_iter_on_array)]
index d720f10d117c097ad2df27ae1ac45e9b63e971ee..cdec2808f1d4142e103fa9a068307c93e3579262 100644 (file)
@@ -1,5 +1,5 @@
 error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
-  --> $DIR/rename.rs:31:9
+  --> $DIR/rename.rs:34:9
    |
 LL | #![warn(clippy::stutter)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
@@ -7,178 +7,190 @@ LL | #![warn(clippy::stutter)]
    = note: `-D renamed-and-removed-lints` implied by `-D warnings`
 
 error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
-  --> $DIR/rename.rs:32:9
+  --> $DIR/rename.rs:35:9
    |
 LL | #![warn(clippy::new_without_default_derive)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
 
 error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
-  --> $DIR/rename.rs:33:9
+  --> $DIR/rename.rs:36:9
    |
 LL | #![warn(clippy::const_static_lifetime)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
 
 error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
-  --> $DIR/rename.rs:34:9
+  --> $DIR/rename.rs:37:9
    |
 LL | #![warn(clippy::cyclomatic_complexity)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
 
 error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
-  --> $DIR/rename.rs:35:9
+  --> $DIR/rename.rs:38:9
    |
 LL | #![warn(clippy::option_and_then_some)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
 
 error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
-  --> $DIR/rename.rs:36:9
+  --> $DIR/rename.rs:39:9
    |
 LL | #![warn(clippy::box_vec)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
 
 error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
-  --> $DIR/rename.rs:37:9
+  --> $DIR/rename.rs:40:9
    |
 LL | #![warn(clippy::block_in_if_condition_expr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 
 error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
-  --> $DIR/rename.rs:38:9
+  --> $DIR/rename.rs:41:9
    |
 LL | #![warn(clippy::block_in_if_condition_stmt)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 
 error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:39:9
+  --> $DIR/rename.rs:42:9
    |
 LL | #![warn(clippy::option_map_unwrap_or)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:40:9
+  --> $DIR/rename.rs:43:9
    |
 LL | #![warn(clippy::option_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:41:9
+  --> $DIR/rename.rs:44:9
    |
 LL | #![warn(clippy::result_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> $DIR/rename.rs:42:9
+  --> $DIR/rename.rs:45:9
    |
 LL | #![warn(clippy::option_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> $DIR/rename.rs:43:9
+  --> $DIR/rename.rs:46:9
    |
 LL | #![warn(clippy::result_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
-  --> $DIR/rename.rs:44:9
+  --> $DIR/rename.rs:47:9
    |
 LL | #![warn(clippy::option_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
-  --> $DIR/rename.rs:45:9
+  --> $DIR/rename.rs:48:9
    |
 LL | #![warn(clippy::result_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
-  --> $DIR/rename.rs:46:9
+  --> $DIR/rename.rs:49:9
    |
 LL | #![warn(clippy::for_loop_over_option)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
 
 error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
-  --> $DIR/rename.rs:47:9
+  --> $DIR/rename.rs:50:9
    |
 LL | #![warn(clippy::for_loop_over_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
 
 error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
-  --> $DIR/rename.rs:48:9
+  --> $DIR/rename.rs:51:9
    |
 LL | #![warn(clippy::identity_conversion)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
 
 error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
-  --> $DIR/rename.rs:49:9
+  --> $DIR/rename.rs:52:9
    |
 LL | #![warn(clippy::zero_width_space)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
 
 error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
-  --> $DIR/rename.rs:50:9
+  --> $DIR/rename.rs:53:9
    |
 LL | #![warn(clippy::single_char_push_str)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
 
 error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
-  --> $DIR/rename.rs:51:9
+  --> $DIR/rename.rs:54:9
    |
 LL | #![warn(clippy::if_let_some_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
 
+error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
+  --> $DIR/rename.rs:55:9
+   |
+LL | #![warn(clippy::disallowed_type)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
+
+error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
+  --> $DIR/rename.rs:56:9
+   |
+LL | #![warn(clippy::disallowed_method)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
+
 error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
-  --> $DIR/rename.rs:53:9
+  --> $DIR/rename.rs:58:9
    |
 LL | #![warn(clippy::invalid_ref)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 
 error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
-  --> $DIR/rename.rs:54:9
+  --> $DIR/rename.rs:59:9
    |
 LL | #![warn(clippy::into_iter_on_array)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 
 error: lint `clippy::unused_label` has been renamed to `unused_labels`
-  --> $DIR/rename.rs:55:9
+  --> $DIR/rename.rs:60:9
    |
 LL | #![warn(clippy::unused_label)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 
 error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
-  --> $DIR/rename.rs:56:9
+  --> $DIR/rename.rs:61:9
    |
 LL | #![warn(clippy::drop_bounds)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 
 error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
-  --> $DIR/rename.rs:57:9
+  --> $DIR/rename.rs:62:9
    |
 LL | #![warn(clippy::temporary_cstring_as_ptr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 
 error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
-  --> $DIR/rename.rs:58:9
+  --> $DIR/rename.rs:63:9
    |
 LL | #![warn(clippy::panic_params)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
 
 error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
-  --> $DIR/rename.rs:59:9
+  --> $DIR/rename.rs:64:9
    |
 LL | #![warn(clippy::unknown_clippy_lints)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 
 error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
-  --> $DIR/rename.rs:60:9
+  --> $DIR/rename.rs:65:9
    |
 LL | #![warn(clippy::invalid_atomic_ordering)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
 
 error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
-  --> $DIR/rename.rs:61:9
+  --> $DIR/rename.rs:66:9
    |
 LL | #![warn(clippy::mem_discriminant_non_enum)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
 
-error: aborting due to 30 previous errors
+error: aborting due to 32 previous errors
 
index 0f9139b41b99dae76e7d06f5d1f8d900ec82b784..c32c3dd9880f6af59f7578462a76149179560f54 100644 (file)
@@ -1,4 +1,4 @@
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:20:13
    |
 LL |             fn foo() {}
@@ -11,7 +11,7 @@ note: existing `foo` defined here
 LL |             fn foo() {}
    |             ^^^^^^^^^^^
 
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:44:13
    |
 LL |             fn foo() {}
@@ -23,7 +23,7 @@ note: existing `foo` defined here
 LL |             fn foo() {}
    |             ^^^^^^^^^^^
 
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:58:13
    |
 LL |             fn foo() {}
@@ -35,7 +35,7 @@ note: existing `foo` defined here
 LL |         impl T1 for S {}
    |         ^^^^^^^^^^^^^^^^
 
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:70:13
    |
 LL |             fn foo() {}
@@ -47,7 +47,7 @@ note: existing `foo` defined here
 LL |         impl T1 for S {}
    |         ^^^^^^^^^^^^^^^^
 
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:34:13
    |
 LL |             fn clone() {}
index 72bc6ef35d317b02f9c5300affc0e6923fdd7790..72f335153c1b1282ec812f1fe09b56d6f97a2e48 100644 (file)
@@ -36,6 +36,9 @@ fn main() {
     // check that we don't lint if `find()` is called with
     // `Pattern` that is not a string
     let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some();
+
+    let some_closure = |x: &u32| *x == 0;
+    let _ = (0..1).find(some_closure).is_some();
 }
 
 #[rustfmt::skip]
@@ -70,4 +73,7 @@ fn is_none() {
     // check that we don't lint if `find()` is called with
     // `Pattern` that is not a string
     let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_none();
+
+    let some_closure = |x: &u32| *x == 0;
+    let _ = (0..1).find(some_closure).is_none();
 }
index f3c758e451ef1436047b24be7d696156b4c075fb..54760545bcedc38cfc536fc63ea90b305048a1a4 100644 (file)
@@ -35,8 +35,14 @@ LL | |                    ).is_some();
    |
    = help: this is more succinctly expressed by calling `any()`
 
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some.rs:41:20
+   |
+LL |     let _ = (0..1).find(some_closure).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(some_closure)`
+
 error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some.rs:48:13
+  --> $DIR/search_is_some.rs:51:13
    |
 LL |       let _ = v.iter().find(|&x| {
    |  _____________^
@@ -48,7 +54,7 @@ LL | |                    ).is_none();
    = help: this is more succinctly expressed by calling `any()` with negation
 
 error: called `is_none()` after searching an `Iterator` with `position`
-  --> $DIR/search_is_some.rs:54:13
+  --> $DIR/search_is_some.rs:57:13
    |
 LL |       let _ = v.iter().position(|&x| {
    |  _____________^
@@ -60,7 +66,7 @@ LL | |                    ).is_none();
    = help: this is more succinctly expressed by calling `any()` with negation
 
 error: called `is_none()` after searching an `Iterator` with `rposition`
-  --> $DIR/search_is_some.rs:60:13
+  --> $DIR/search_is_some.rs:63:13
    |
 LL |       let _ = v.iter().rposition(|&x| {
    |  _____________^
@@ -71,5 +77,11 @@ LL | |                    ).is_none();
    |
    = help: this is more succinctly expressed by calling `any()` with negation
 
-error: aborting due to 6 previous errors
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some.rs:78:13
+   |
+LL |     let _ = (0..1).find(some_closure).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(some_closure)`
+
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable.fixed
deleted file mode 100644 (file)
index 62ff16f..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-// run-rustfix
-#![allow(dead_code)]
-#![warn(clippy::search_is_some)]
-
-fn main() {
-    let v = vec![3, 2, 1, 0, -1, -2, -3];
-    let y = &&42;
-
-    // Check `find().is_some()`, single-line case.
-    let _ = v.iter().any(|x| *x < 0);
-    let _ = (0..1).any(|x| **y == x); // one dereference less
-    let _ = (0..1).any(|x| x == 0);
-    let _ = v.iter().any(|x| *x == 0);
-
-    // Check `position().is_some()`, single-line case.
-    let _ = v.iter().any(|&x| x < 0);
-
-    // Check `rposition().is_some()`, single-line case.
-    let _ = v.iter().any(|&x| x < 0);
-
-    let s1 = String::from("hello world");
-    let s2 = String::from("world");
-    // caller of `find()` is a `&`static str`
-    let _ = "hello world".contains("world");
-    let _ = "hello world".contains(&s2);
-    let _ = "hello world".contains(&s2[2..]);
-    // caller of `find()` is a `String`
-    let _ = s1.contains("world");
-    let _ = s1.contains(&s2);
-    let _ = s1.contains(&s2[2..]);
-    // caller of `find()` is slice of `String`
-    let _ = s1[2..].contains("world");
-    let _ = s1[2..].contains(&s2);
-    let _ = s1[2..].contains(&s2[2..]);
-}
-
-fn is_none() {
-    let v = vec![3, 2, 1, 0, -1, -2, -3];
-    let y = &&42;
-
-    // Check `find().is_none()`, single-line case.
-    let _ = !v.iter().any(|x| *x < 0);
-    let _ = !(0..1).any(|x| **y == x); // one dereference less
-    let _ = !(0..1).any(|x| x == 0);
-    let _ = !v.iter().any(|x| *x == 0);
-
-    // Check `position().is_none()`, single-line case.
-    let _ = !v.iter().any(|&x| x < 0);
-
-    // Check `rposition().is_none()`, single-line case.
-    let _ = !v.iter().any(|&x| x < 0);
-
-    let s1 = String::from("hello world");
-    let s2 = String::from("world");
-
-    // caller of `find()` is a `&`static str`
-    let _ = !"hello world".contains("world");
-    let _ = !"hello world".contains(&s2);
-    let _ = !"hello world".contains(&s2[2..]);
-    // caller of `find()` is a `String`
-    let _ = !s1.contains("world");
-    let _ = !s1.contains(&s2);
-    let _ = !s1.contains(&s2[2..]);
-    // caller of `find()` is slice of `String`
-    let _ = !s1[2..].contains("world");
-    let _ = !s1[2..].contains(&s2);
-    let _ = !s1[2..].contains(&s2[2..]);
-}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable.rs b/src/tools/clippy/tests/ui/search_is_some_fixable.rs
deleted file mode 100644 (file)
index 8407f71..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-// run-rustfix
-#![allow(dead_code)]
-#![warn(clippy::search_is_some)]
-
-fn main() {
-    let v = vec![3, 2, 1, 0, -1, -2, -3];
-    let y = &&42;
-
-    // Check `find().is_some()`, single-line case.
-    let _ = v.iter().find(|&x| *x < 0).is_some();
-    let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
-    let _ = (0..1).find(|x| *x == 0).is_some();
-    let _ = v.iter().find(|x| **x == 0).is_some();
-
-    // Check `position().is_some()`, single-line case.
-    let _ = v.iter().position(|&x| x < 0).is_some();
-
-    // Check `rposition().is_some()`, single-line case.
-    let _ = v.iter().rposition(|&x| x < 0).is_some();
-
-    let s1 = String::from("hello world");
-    let s2 = String::from("world");
-    // caller of `find()` is a `&`static str`
-    let _ = "hello world".find("world").is_some();
-    let _ = "hello world".find(&s2).is_some();
-    let _ = "hello world".find(&s2[2..]).is_some();
-    // caller of `find()` is a `String`
-    let _ = s1.find("world").is_some();
-    let _ = s1.find(&s2).is_some();
-    let _ = s1.find(&s2[2..]).is_some();
-    // caller of `find()` is slice of `String`
-    let _ = s1[2..].find("world").is_some();
-    let _ = s1[2..].find(&s2).is_some();
-    let _ = s1[2..].find(&s2[2..]).is_some();
-}
-
-fn is_none() {
-    let v = vec![3, 2, 1, 0, -1, -2, -3];
-    let y = &&42;
-
-    // Check `find().is_none()`, single-line case.
-    let _ = v.iter().find(|&x| *x < 0).is_none();
-    let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
-    let _ = (0..1).find(|x| *x == 0).is_none();
-    let _ = v.iter().find(|x| **x == 0).is_none();
-
-    // Check `position().is_none()`, single-line case.
-    let _ = v.iter().position(|&x| x < 0).is_none();
-
-    // Check `rposition().is_none()`, single-line case.
-    let _ = v.iter().rposition(|&x| x < 0).is_none();
-
-    let s1 = String::from("hello world");
-    let s2 = String::from("world");
-
-    // caller of `find()` is a `&`static str`
-    let _ = "hello world".find("world").is_none();
-    let _ = "hello world".find(&s2).is_none();
-    let _ = "hello world".find(&s2[2..]).is_none();
-    // caller of `find()` is a `String`
-    let _ = s1.find("world").is_none();
-    let _ = s1.find(&s2).is_none();
-    let _ = s1.find(&s2[2..]).is_none();
-    // caller of `find()` is slice of `String`
-    let _ = s1[2..].find("world").is_none();
-    let _ = s1[2..].find(&s2).is_none();
-    let _ = s1[2..].find(&s2[2..]).is_none();
-}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable.stderr
deleted file mode 100644 (file)
index bd1b695..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-error: called `is_some()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:10:22
-   |
-LL |     let _ = v.iter().find(|&x| *x < 0).is_some();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)`
-   |
-   = note: `-D clippy::search-is-some` implied by `-D warnings`
-
-error: called `is_some()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:11:20
-   |
-LL |     let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)`
-
-error: called `is_some()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:12:20
-   |
-LL |     let _ = (0..1).find(|x| *x == 0).is_some();
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)`
-
-error: called `is_some()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:13:22
-   |
-LL |     let _ = v.iter().find(|x| **x == 0).is_some();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)`
-
-error: called `is_some()` after searching an `Iterator` with `position`
-  --> $DIR/search_is_some_fixable.rs:16:22
-   |
-LL |     let _ = v.iter().position(|&x| x < 0).is_some();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
-
-error: called `is_some()` after searching an `Iterator` with `rposition`
-  --> $DIR/search_is_some_fixable.rs:19:22
-   |
-LL |     let _ = v.iter().rposition(|&x| x < 0).is_some();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:24:27
-   |
-LL |     let _ = "hello world".find("world").is_some();
-   |                           ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:25:27
-   |
-LL |     let _ = "hello world".find(&s2).is_some();
-   |                           ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:26:27
-   |
-LL |     let _ = "hello world".find(&s2[2..]).is_some();
-   |                           ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:28:16
-   |
-LL |     let _ = s1.find("world").is_some();
-   |                ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:29:16
-   |
-LL |     let _ = s1.find(&s2).is_some();
-   |                ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:30:16
-   |
-LL |     let _ = s1.find(&s2[2..]).is_some();
-   |                ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:32:21
-   |
-LL |     let _ = s1[2..].find("world").is_some();
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:33:21
-   |
-LL |     let _ = s1[2..].find(&s2).is_some();
-   |                     ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:34:21
-   |
-LL |     let _ = s1[2..].find(&s2[2..]).is_some();
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
-
-error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:42:13
-   |
-LL |     let _ = v.iter().find(|&x| *x < 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)`
-
-error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:43:13
-   |
-LL |     let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)`
-
-error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:44:13
-   |
-LL |     let _ = (0..1).find(|x| *x == 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)`
-
-error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:45:13
-   |
-LL |     let _ = v.iter().find(|x| **x == 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)`
-
-error: called `is_none()` after searching an `Iterator` with `position`
-  --> $DIR/search_is_some_fixable.rs:48:13
-   |
-LL |     let _ = v.iter().position(|&x| x < 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
-
-error: called `is_none()` after searching an `Iterator` with `rposition`
-  --> $DIR/search_is_some_fixable.rs:51:13
-   |
-LL |     let _ = v.iter().rposition(|&x| x < 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:57:13
-   |
-LL |     let _ = "hello world".find("world").is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:58:13
-   |
-LL |     let _ = "hello world".find(&s2).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:59:13
-   |
-LL |     let _ = "hello world".find(&s2[2..]).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:61:13
-   |
-LL |     let _ = s1.find("world").is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:62:13
-   |
-LL |     let _ = s1.find(&s2).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:63:13
-   |
-LL |     let _ = s1.find(&s2[2..]).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:65:13
-   |
-LL |     let _ = s1[2..].find("world").is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:66:13
-   |
-LL |     let _ = s1[2..].find(&s2).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:67:13
-   |
-LL |     let _ = s1[2..].find(&s2[2..]).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])`
-
-error: aborting due to 30 previous errors
-
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed
new file mode 100644 (file)
index 0000000..6831fb2
--- /dev/null
@@ -0,0 +1,216 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+    // Check `find().is_none()`, single-line case.
+    let _ = !v.iter().any(|x| *x < 0);
+    let _ = !(0..1).any(|x| **y == x); // one dereference less
+    let _ = !(0..1).any(|x| x == 0);
+    let _ = !v.iter().any(|x| *x == 0);
+    let _ = !(4..5).any(|x| x == 1 || x == 3 || x == 5);
+    let _ = !(1..3).any(|x| [1, 2, 3].contains(&x));
+    let _ = !(1..3).any(|x| x == 0 || [1, 2, 3].contains(&x));
+    let _ = !(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0);
+    let _ = !(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1);
+
+    // Check `position().is_none()`, single-line case.
+    let _ = !v.iter().any(|&x| x < 0);
+
+    // Check `rposition().is_none()`, single-line case.
+    let _ = !v.iter().any(|&x| x < 0);
+
+    let s1 = String::from("hello world");
+    let s2 = String::from("world");
+
+    // caller of `find()` is a `&`static str`
+    let _ = !"hello world".contains("world");
+    let _ = !"hello world".contains(&s2);
+    let _ = !"hello world".contains(&s2[2..]);
+    // caller of `find()` is a `String`
+    let _ = !s1.contains("world");
+    let _ = !s1.contains(&s2);
+    let _ = !s1.contains(&s2[2..]);
+    // caller of `find()` is slice of `String`
+    let _ = !s1[2..].contains("world");
+    let _ = !s1[2..].contains(&s2);
+    let _ = !s1[2..].contains(&s2[2..]);
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+    struct Player {
+        hand: Vec<usize>,
+    }
+    fn filter() {
+        let p = Player {
+            hand: vec![1, 2, 3, 4, 5],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|c| !filter_hand.iter().any(|cc| c == &cc))
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    struct PlayerTuple {
+        hand: Vec<(usize, char)>,
+    }
+    fn filter_tuple() {
+        let p = PlayerTuple {
+            hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|(c, _)| !filter_hand.iter().any(|cc| c == cc))
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    fn field_projection() {
+        struct Foo {
+            foo: i32,
+            bar: u32,
+        }
+        let vfoo = vec![Foo { foo: 1, bar: 2 }];
+        let _ = !vfoo.iter().any(|v| v.foo == 1 && v.bar == 2);
+
+        let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+        let _ = !vfoo
+            .iter().any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+    }
+
+    fn index_projection() {
+        let vfoo = vec![[0, 1, 2, 3]];
+        let _ = !vfoo.iter().any(|a| a[0] == 42);
+    }
+
+    #[allow(clippy::match_like_matches_macro)]
+    fn slice_projection() {
+        let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+        let _ = !vfoo.iter().any(|sub| sub[1..4].len() == 3);
+    }
+
+    fn please(x: &u32) -> bool {
+        *x == 9
+    }
+
+    fn deref_enough(x: u32) -> bool {
+        x == 78
+    }
+
+    fn arg_no_deref(x: &&u32) -> bool {
+        **x == 78
+    }
+
+    fn more_projections() {
+        let x = 19;
+        let ppx: &u32 = &x;
+        let _ = ![ppx].iter().any(|ppp_x: &&u32| please(ppp_x));
+        let _ = ![String::from("Hey hey")].iter().any(|s| s.len() == 2);
+
+        let v = vec![3, 2, 1, 0];
+        let _ = !v.iter().any(|x| deref_enough(*x));
+        let _ = !v.iter().any(|x: &u32| deref_enough(*x));
+
+        #[allow(clippy::redundant_closure)]
+        let _ = !v.iter().any(|x| arg_no_deref(&x));
+        #[allow(clippy::redundant_closure)]
+        let _ = !v.iter().any(|x: &u32| arg_no_deref(&x));
+    }
+
+    fn field_index_projection() {
+        struct FooDouble {
+            bar: Vec<Vec<i32>>,
+        }
+        struct Foo {
+            bar: Vec<i32>,
+        }
+        struct FooOuter {
+            inner: Foo,
+            inner_double: FooDouble,
+        }
+        let vfoo = vec![FooOuter {
+            inner: Foo { bar: vec![0, 1, 2, 3] },
+            inner_double: FooDouble {
+                bar: vec![vec![0, 1, 2, 3]],
+            },
+        }];
+        let _ = !vfoo
+            .iter().any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+    }
+
+    fn index_field_projection() {
+        struct Foo {
+            bar: i32,
+        }
+        struct FooOuter {
+            inner: Vec<Foo>,
+        }
+        let vfoo = vec![FooOuter {
+            inner: vec![Foo { bar: 0 }],
+        }];
+        let _ = !vfoo.iter().any(|v| v.inner[0].bar == 2);
+    }
+
+    fn double_deref_index_projection() {
+        let vfoo = vec![&&[0, 1, 2, 3]];
+        let _ = !vfoo.iter().any(|x| (**x)[0] == 9);
+    }
+
+    fn method_call_by_ref() {
+        struct Foo {
+            bar: u32,
+        }
+        impl Foo {
+            pub fn by_ref(&self, x: &u32) -> bool {
+                *x == self.bar
+            }
+        }
+        let vfoo = vec![Foo { bar: 1 }];
+        let _ = !vfoo.iter().any(|v| v.by_ref(&v.bar));
+    }
+
+    fn ref_bindings() {
+        let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+        let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+    }
+
+    fn test_string_1(s: &str) -> bool {
+        s.is_empty()
+    }
+
+    fn test_u32_1(s: &u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn test_u32_2(s: u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn projection_in_args_test() {
+        // Index projections
+        let lst = &[String::from("Hello"), String::from("world")];
+        let v: Vec<&[String]> = vec![lst];
+        let _ = !v.iter().any(|s| s[0].is_empty());
+        let _ = !v.iter().any(|s| test_string_1(&s[0]));
+
+        // Field projections
+        struct FieldProjection<'a> {
+            field: &'a u32,
+        }
+        let field = 123456789;
+        let instance = FieldProjection { field: &field };
+        let v = vec![instance];
+        let _ = !v.iter().any(|fp| fp.field.is_power_of_two());
+        let _ = !v.iter().any(|fp| test_u32_1(fp.field));
+        let _ = !v.iter().any(|fp| test_u32_2(*fp.field));
+    }
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs
new file mode 100644 (file)
index 0000000..778f4f6
--- /dev/null
@@ -0,0 +1,222 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+    // Check `find().is_none()`, single-line case.
+    let _ = v.iter().find(|&x| *x < 0).is_none();
+    let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
+    let _ = (0..1).find(|x| *x == 0).is_none();
+    let _ = v.iter().find(|x| **x == 0).is_none();
+    let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_none();
+    let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_none();
+    let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_none();
+    let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_none();
+    let _ = (1..3)
+        .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+        .is_none();
+
+    // Check `position().is_none()`, single-line case.
+    let _ = v.iter().position(|&x| x < 0).is_none();
+
+    // Check `rposition().is_none()`, single-line case.
+    let _ = v.iter().rposition(|&x| x < 0).is_none();
+
+    let s1 = String::from("hello world");
+    let s2 = String::from("world");
+
+    // caller of `find()` is a `&`static str`
+    let _ = "hello world".find("world").is_none();
+    let _ = "hello world".find(&s2).is_none();
+    let _ = "hello world".find(&s2[2..]).is_none();
+    // caller of `find()` is a `String`
+    let _ = s1.find("world").is_none();
+    let _ = s1.find(&s2).is_none();
+    let _ = s1.find(&s2[2..]).is_none();
+    // caller of `find()` is slice of `String`
+    let _ = s1[2..].find("world").is_none();
+    let _ = s1[2..].find(&s2).is_none();
+    let _ = s1[2..].find(&s2[2..]).is_none();
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+    struct Player {
+        hand: Vec<usize>,
+    }
+    fn filter() {
+        let p = Player {
+            hand: vec![1, 2, 3, 4, 5],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|c| filter_hand.iter().find(|cc| c == cc).is_none())
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    struct PlayerTuple {
+        hand: Vec<(usize, char)>,
+    }
+    fn filter_tuple() {
+        let p = PlayerTuple {
+            hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_none())
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    fn field_projection() {
+        struct Foo {
+            foo: i32,
+            bar: u32,
+        }
+        let vfoo = vec![Foo { foo: 1, bar: 2 }];
+        let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_none();
+
+        let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+        let _ = vfoo
+            .iter()
+            .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+            .is_none();
+    }
+
+    fn index_projection() {
+        let vfoo = vec![[0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|a| a[0] == 42).is_none();
+    }
+
+    #[allow(clippy::match_like_matches_macro)]
+    fn slice_projection() {
+        let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_none();
+    }
+
+    fn please(x: &u32) -> bool {
+        *x == 9
+    }
+
+    fn deref_enough(x: u32) -> bool {
+        x == 78
+    }
+
+    fn arg_no_deref(x: &&u32) -> bool {
+        **x == 78
+    }
+
+    fn more_projections() {
+        let x = 19;
+        let ppx: &u32 = &x;
+        let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_none();
+        let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_none();
+
+        let v = vec![3, 2, 1, 0];
+        let _ = v.iter().find(|x| deref_enough(**x)).is_none();
+        let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_none();
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x| arg_no_deref(x)).is_none();
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_none();
+    }
+
+    fn field_index_projection() {
+        struct FooDouble {
+            bar: Vec<Vec<i32>>,
+        }
+        struct Foo {
+            bar: Vec<i32>,
+        }
+        struct FooOuter {
+            inner: Foo,
+            inner_double: FooDouble,
+        }
+        let vfoo = vec![FooOuter {
+            inner: Foo { bar: vec![0, 1, 2, 3] },
+            inner_double: FooDouble {
+                bar: vec![vec![0, 1, 2, 3]],
+            },
+        }];
+        let _ = vfoo
+            .iter()
+            .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+            .is_none();
+    }
+
+    fn index_field_projection() {
+        struct Foo {
+            bar: i32,
+        }
+        struct FooOuter {
+            inner: Vec<Foo>,
+        }
+        let vfoo = vec![FooOuter {
+            inner: vec![Foo { bar: 0 }],
+        }];
+        let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_none();
+    }
+
+    fn double_deref_index_projection() {
+        let vfoo = vec![&&[0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_none();
+    }
+
+    fn method_call_by_ref() {
+        struct Foo {
+            bar: u32,
+        }
+        impl Foo {
+            pub fn by_ref(&self, x: &u32) -> bool {
+                *x == self.bar
+            }
+        }
+        let vfoo = vec![Foo { bar: 1 }];
+        let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none();
+    }
+
+    fn ref_bindings() {
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none();
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none();
+    }
+
+    fn test_string_1(s: &String) -> bool {
+        s.is_empty()
+    }
+
+    fn test_u32_1(s: &u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn test_u32_2(s: u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn projection_in_args_test() {
+        // Index projections
+        let lst = &[String::from("Hello"), String::from("world")];
+        let v: Vec<&[String]> = vec![lst];
+        let _ = v.iter().find(|s| s[0].is_empty()).is_none();
+        let _ = v.iter().find(|s| test_string_1(&s[0])).is_none();
+
+        // Field projections
+        struct FieldProjection<'a> {
+            field: &'a u32,
+        }
+        let field = 123456789;
+        let instance = FieldProjection { field: &field };
+        let v = vec![instance];
+        let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none();
+        let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none();
+        let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
+    }
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr
new file mode 100644 (file)
index 0000000..7c5e5eb
--- /dev/null
@@ -0,0 +1,293 @@
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:10:13
+   |
+LL |     let _ = v.iter().find(|&x| *x < 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)`
+   |
+   = note: `-D clippy::search-is-some` implied by `-D warnings`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:11:13
+   |
+LL |     let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:12:13
+   |
+LL |     let _ = (0..1).find(|x| *x == 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:13:13
+   |
+LL |     let _ = v.iter().find(|x| **x == 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:14:13
+   |
+LL |     let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(4..5).any(|x| x == 1 || x == 3 || x == 5)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:15:13
+   |
+LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:16:13
+   |
+LL |     let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| x == 0 || [1, 2, 3].contains(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:17:13
+   |
+LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:18:13
+   |
+LL |       let _ = (1..3)
+   |  _____________^
+LL | |         .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+LL | |         .is_none();
+   | |__________________^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)`
+
+error: called `is_none()` after searching an `Iterator` with `position`
+  --> $DIR/search_is_some_fixable_none.rs:23:13
+   |
+LL |     let _ = v.iter().position(|&x| x < 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
+
+error: called `is_none()` after searching an `Iterator` with `rposition`
+  --> $DIR/search_is_some_fixable_none.rs:26:13
+   |
+LL |     let _ = v.iter().rposition(|&x| x < 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:32:13
+   |
+LL |     let _ = "hello world".find("world").is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:33:13
+   |
+LL |     let _ = "hello world".find(&s2).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:34:13
+   |
+LL |     let _ = "hello world".find(&s2[2..]).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:36:13
+   |
+LL |     let _ = s1.find("world").is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:37:13
+   |
+LL |     let _ = s1.find(&s2).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:38:13
+   |
+LL |     let _ = s1.find(&s2[2..]).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:40:13
+   |
+LL |     let _ = s1[2..].find("world").is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:41:13
+   |
+LL |     let _ = s1[2..].find(&s2).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:42:13
+   |
+LL |     let _ = s1[2..].find(&s2[2..]).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:58:25
+   |
+LL |             .filter(|c| filter_hand.iter().find(|cc| c == cc).is_none())
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!filter_hand.iter().any(|cc| c == &cc)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:74:30
+   |
+LL |             .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_none())
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!filter_hand.iter().any(|cc| c == cc)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:85:17
+   |
+LL |         let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.foo == 1 && v.bar == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:88:17
+   |
+LL |           let _ = vfoo
+   |  _________________^
+LL | |             .iter()
+LL | |             .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+LL | |             .is_none();
+   | |______________________^
+   |
+help: use `!_.any()` instead
+   |
+LL ~         let _ = !vfoo
+LL ~             .iter().any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+   |
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:96:17
+   |
+LL |         let _ = vfoo.iter().find(|a| a[0] == 42).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|a| a[0] == 42)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:102:17
+   |
+LL |         let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|sub| sub[1..4].len() == 3)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:120:17
+   |
+LL |         let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![ppx].iter().any(|ppp_x: &&u32| please(ppp_x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:121:17
+   |
+LL |         let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![String::from("Hey hey")].iter().any(|s| s.len() == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:124:17
+   |
+LL |         let _ = v.iter().find(|x| deref_enough(**x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| deref_enough(*x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:125:17
+   |
+LL |         let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x: &u32| deref_enough(*x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:128:17
+   |
+LL |         let _ = v.iter().find(|x| arg_no_deref(x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| arg_no_deref(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:130:17
+   |
+LL |         let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x: &u32| arg_no_deref(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:150:17
+   |
+LL |           let _ = vfoo
+   |  _________________^
+LL | |             .iter()
+LL | |             .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+LL | |             .is_none();
+   | |______________________^
+   |
+help: use `!_.any()` instead
+   |
+LL ~         let _ = !vfoo
+LL ~             .iter().any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+   |
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:166:17
+   |
+LL |         let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.inner[0].bar == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:171:17
+   |
+LL |         let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|x| (**x)[0] == 9)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:184:17
+   |
+LL |         let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.by_ref(&v.bar))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:188:17
+   |
+LL |         let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:189:17
+   |
+LL |         let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)`
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+  --> $DIR/search_is_some_fixable_none.rs:192:25
+   |
+LL |     fn test_string_1(s: &String) -> bool {
+   |                         ^^^^^^^ help: change this to: `&str`
+   |
+   = note: `-D clippy::ptr-arg` implied by `-D warnings`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:208:17
+   |
+LL |         let _ = v.iter().find(|s| s[0].is_empty()).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| s[0].is_empty())`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:209:17
+   |
+LL |         let _ = v.iter().find(|s| test_string_1(&s[0])).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| test_string_1(&s[0]))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:218:17
+   |
+LL |         let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| fp.field.is_power_of_two())`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:219:17
+   |
+LL |         let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_1(fp.field))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:220:17
+   |
+LL |         let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_2(*fp.field))`
+
+error: aborting due to 44 previous errors
+
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed
new file mode 100644 (file)
index 0000000..7c940a2
--- /dev/null
@@ -0,0 +1,218 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+    // Check `find().is_some()`, single-line case.
+    let _ = v.iter().any(|x| *x < 0);
+    let _ = (0..1).any(|x| **y == x); // one dereference less
+    let _ = (0..1).any(|x| x == 0);
+    let _ = v.iter().any(|x| *x == 0);
+    let _ = (4..5).any(|x| x == 1 || x == 3 || x == 5);
+    let _ = (1..3).any(|x| [1, 2, 3].contains(&x));
+    let _ = (1..3).any(|x| x == 0 || [1, 2, 3].contains(&x));
+    let _ = (1..3).any(|x| [1, 2, 3].contains(&x) || x == 0);
+    let _ = (1..3)
+        .any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1);
+
+    // Check `position().is_some()`, single-line case.
+    let _ = v.iter().any(|&x| x < 0);
+
+    // Check `rposition().is_some()`, single-line case.
+    let _ = v.iter().any(|&x| x < 0);
+
+    let s1 = String::from("hello world");
+    let s2 = String::from("world");
+    // caller of `find()` is a `&`static str`
+    let _ = "hello world".contains("world");
+    let _ = "hello world".contains(&s2);
+    let _ = "hello world".contains(&s2[2..]);
+    // caller of `find()` is a `String`
+    let _ = s1.contains("world");
+    let _ = s1.contains(&s2);
+    let _ = s1.contains(&s2[2..]);
+    // caller of `find()` is slice of `String`
+    let _ = s1[2..].contains("world");
+    let _ = s1[2..].contains(&s2);
+    let _ = s1[2..].contains(&s2[2..]);
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+    struct Player {
+        hand: Vec<usize>,
+    }
+    fn filter() {
+        let p = Player {
+            hand: vec![1, 2, 3, 4, 5],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|c| filter_hand.iter().any(|cc| c == &cc))
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    struct PlayerTuple {
+        hand: Vec<(usize, char)>,
+    }
+    fn filter_tuple() {
+        let p = PlayerTuple {
+            hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|(c, _)| filter_hand.iter().any(|cc| c == cc))
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    fn field_projection() {
+        struct Foo {
+            foo: i32,
+            bar: u32,
+        }
+        let vfoo = vec![Foo { foo: 1, bar: 2 }];
+        let _ = vfoo.iter().any(|v| v.foo == 1 && v.bar == 2);
+
+        let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+        let _ = vfoo
+            .iter()
+            .any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+    }
+
+    fn index_projection() {
+        let vfoo = vec![[0, 1, 2, 3]];
+        let _ = vfoo.iter().any(|a| a[0] == 42);
+    }
+
+    #[allow(clippy::match_like_matches_macro)]
+    fn slice_projection() {
+        let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+        let _ = vfoo.iter().any(|sub| sub[1..4].len() == 3);
+    }
+
+    fn please(x: &u32) -> bool {
+        *x == 9
+    }
+
+    fn deref_enough(x: u32) -> bool {
+        x == 78
+    }
+
+    fn arg_no_deref(x: &&u32) -> bool {
+        **x == 78
+    }
+
+    fn more_projections() {
+        let x = 19;
+        let ppx: &u32 = &x;
+        let _ = [ppx].iter().any(|ppp_x: &&u32| please(ppp_x));
+        let _ = [String::from("Hey hey")].iter().any(|s| s.len() == 2);
+
+        let v = vec![3, 2, 1, 0];
+        let _ = v.iter().any(|x| deref_enough(*x));
+        let _ = v.iter().any(|x: &u32| deref_enough(*x));
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().any(|x| arg_no_deref(&x));
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().any(|x: &u32| arg_no_deref(&x));
+    }
+
+    fn field_index_projection() {
+        struct FooDouble {
+            bar: Vec<Vec<i32>>,
+        }
+        struct Foo {
+            bar: Vec<i32>,
+        }
+        struct FooOuter {
+            inner: Foo,
+            inner_double: FooDouble,
+        }
+        let vfoo = vec![FooOuter {
+            inner: Foo { bar: vec![0, 1, 2, 3] },
+            inner_double: FooDouble {
+                bar: vec![vec![0, 1, 2, 3]],
+            },
+        }];
+        let _ = vfoo
+            .iter()
+            .any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+    }
+
+    fn index_field_projection() {
+        struct Foo {
+            bar: i32,
+        }
+        struct FooOuter {
+            inner: Vec<Foo>,
+        }
+        let vfoo = vec![FooOuter {
+            inner: vec![Foo { bar: 0 }],
+        }];
+        let _ = vfoo.iter().any(|v| v.inner[0].bar == 2);
+    }
+
+    fn double_deref_index_projection() {
+        let vfoo = vec![&&[0, 1, 2, 3]];
+        let _ = vfoo.iter().any(|x| (**x)[0] == 9);
+    }
+
+    fn method_call_by_ref() {
+        struct Foo {
+            bar: u32,
+        }
+        impl Foo {
+            pub fn by_ref(&self, x: &u32) -> bool {
+                *x == self.bar
+            }
+        }
+        let vfoo = vec![Foo { bar: 1 }];
+        let _ = vfoo.iter().any(|v| v.by_ref(&v.bar));
+    }
+
+    fn ref_bindings() {
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+    }
+
+    fn test_string_1(s: &str) -> bool {
+        s.is_empty()
+    }
+
+    fn test_u32_1(s: &u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn test_u32_2(s: u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn projection_in_args_test() {
+        // Index projections
+        let lst = &[String::from("Hello"), String::from("world")];
+        let v: Vec<&[String]> = vec![lst];
+        let _ = v.iter().any(|s| s[0].is_empty());
+        let _ = v.iter().any(|s| test_string_1(&s[0]));
+
+        // Field projections
+        struct FieldProjection<'a> {
+            field: &'a u32,
+        }
+        let field = 123456789;
+        let instance = FieldProjection { field: &field };
+        let v = vec![instance];
+        let _ = v.iter().any(|fp| fp.field.is_power_of_two());
+        let _ = v.iter().any(|fp| test_u32_1(fp.field));
+        let _ = v.iter().any(|fp| test_u32_2(*fp.field));
+    }
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs
new file mode 100644 (file)
index 0000000..241641f
--- /dev/null
@@ -0,0 +1,221 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+    // Check `find().is_some()`, single-line case.
+    let _ = v.iter().find(|&x| *x < 0).is_some();
+    let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
+    let _ = (0..1).find(|x| *x == 0).is_some();
+    let _ = v.iter().find(|x| **x == 0).is_some();
+    let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some();
+    let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some();
+    let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some();
+    let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some();
+    let _ = (1..3)
+        .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+        .is_some();
+
+    // Check `position().is_some()`, single-line case.
+    let _ = v.iter().position(|&x| x < 0).is_some();
+
+    // Check `rposition().is_some()`, single-line case.
+    let _ = v.iter().rposition(|&x| x < 0).is_some();
+
+    let s1 = String::from("hello world");
+    let s2 = String::from("world");
+    // caller of `find()` is a `&`static str`
+    let _ = "hello world".find("world").is_some();
+    let _ = "hello world".find(&s2).is_some();
+    let _ = "hello world".find(&s2[2..]).is_some();
+    // caller of `find()` is a `String`
+    let _ = s1.find("world").is_some();
+    let _ = s1.find(&s2).is_some();
+    let _ = s1.find(&s2[2..]).is_some();
+    // caller of `find()` is slice of `String`
+    let _ = s1[2..].find("world").is_some();
+    let _ = s1[2..].find(&s2).is_some();
+    let _ = s1[2..].find(&s2[2..]).is_some();
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+    struct Player {
+        hand: Vec<usize>,
+    }
+    fn filter() {
+        let p = Player {
+            hand: vec![1, 2, 3, 4, 5],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some())
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    struct PlayerTuple {
+        hand: Vec<(usize, char)>,
+    }
+    fn filter_tuple() {
+        let p = PlayerTuple {
+            hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some())
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    fn field_projection() {
+        struct Foo {
+            foo: i32,
+            bar: u32,
+        }
+        let vfoo = vec![Foo { foo: 1, bar: 2 }];
+        let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some();
+
+        let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+        let _ = vfoo
+            .iter()
+            .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+            .is_some();
+    }
+
+    fn index_projection() {
+        let vfoo = vec![[0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|a| a[0] == 42).is_some();
+    }
+
+    #[allow(clippy::match_like_matches_macro)]
+    fn slice_projection() {
+        let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some();
+    }
+
+    fn please(x: &u32) -> bool {
+        *x == 9
+    }
+
+    fn deref_enough(x: u32) -> bool {
+        x == 78
+    }
+
+    fn arg_no_deref(x: &&u32) -> bool {
+        **x == 78
+    }
+
+    fn more_projections() {
+        let x = 19;
+        let ppx: &u32 = &x;
+        let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some();
+        let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some();
+
+        let v = vec![3, 2, 1, 0];
+        let _ = v.iter().find(|x| deref_enough(**x)).is_some();
+        let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some();
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x| arg_no_deref(x)).is_some();
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some();
+    }
+
+    fn field_index_projection() {
+        struct FooDouble {
+            bar: Vec<Vec<i32>>,
+        }
+        struct Foo {
+            bar: Vec<i32>,
+        }
+        struct FooOuter {
+            inner: Foo,
+            inner_double: FooDouble,
+        }
+        let vfoo = vec![FooOuter {
+            inner: Foo { bar: vec![0, 1, 2, 3] },
+            inner_double: FooDouble {
+                bar: vec![vec![0, 1, 2, 3]],
+            },
+        }];
+        let _ = vfoo
+            .iter()
+            .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+            .is_some();
+    }
+
+    fn index_field_projection() {
+        struct Foo {
+            bar: i32,
+        }
+        struct FooOuter {
+            inner: Vec<Foo>,
+        }
+        let vfoo = vec![FooOuter {
+            inner: vec![Foo { bar: 0 }],
+        }];
+        let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some();
+    }
+
+    fn double_deref_index_projection() {
+        let vfoo = vec![&&[0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some();
+    }
+
+    fn method_call_by_ref() {
+        struct Foo {
+            bar: u32,
+        }
+        impl Foo {
+            pub fn by_ref(&self, x: &u32) -> bool {
+                *x == self.bar
+            }
+        }
+        let vfoo = vec![Foo { bar: 1 }];
+        let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some();
+    }
+
+    fn ref_bindings() {
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some();
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
+    }
+
+    fn test_string_1(s: &String) -> bool {
+        s.is_empty()
+    }
+
+    fn test_u32_1(s: &u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn test_u32_2(s: u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn projection_in_args_test() {
+        // Index projections
+        let lst = &[String::from("Hello"), String::from("world")];
+        let v: Vec<&[String]> = vec![lst];
+        let _ = v.iter().find(|s| s[0].is_empty()).is_some();
+        let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
+
+        // Field projections
+        struct FieldProjection<'a> {
+            field: &'a u32,
+        }
+        let field = 123456789;
+        let instance = FieldProjection { field: &field };
+        let v = vec![instance];
+        let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
+        let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
+        let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
+    }
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr
new file mode 100644 (file)
index 0000000..9212c6e
--- /dev/null
@@ -0,0 +1,276 @@
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:10:22
+   |
+LL |     let _ = v.iter().find(|&x| *x < 0).is_some();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)`
+   |
+   = note: `-D clippy::search-is-some` implied by `-D warnings`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:11:20
+   |
+LL |     let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:12:20
+   |
+LL |     let _ = (0..1).find(|x| *x == 0).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:13:22
+   |
+LL |     let _ = v.iter().find(|x| **x == 0).is_some();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:14:20
+   |
+LL |     let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 1 || x == 3 || x == 5)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:15:20
+   |
+LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:16:20
+   |
+LL |     let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0 || [1, 2, 3].contains(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:17:20
+   |
+LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:19:10
+   |
+LL |           .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+   |  __________^
+LL | |         .is_some();
+   | |__________________^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)`
+
+error: called `is_some()` after searching an `Iterator` with `position`
+  --> $DIR/search_is_some_fixable_some.rs:23:22
+   |
+LL |     let _ = v.iter().position(|&x| x < 0).is_some();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
+
+error: called `is_some()` after searching an `Iterator` with `rposition`
+  --> $DIR/search_is_some_fixable_some.rs:26:22
+   |
+LL |     let _ = v.iter().rposition(|&x| x < 0).is_some();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:31:27
+   |
+LL |     let _ = "hello world".find("world").is_some();
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:32:27
+   |
+LL |     let _ = "hello world".find(&s2).is_some();
+   |                           ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:33:27
+   |
+LL |     let _ = "hello world".find(&s2[2..]).is_some();
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:35:16
+   |
+LL |     let _ = s1.find("world").is_some();
+   |                ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:36:16
+   |
+LL |     let _ = s1.find(&s2).is_some();
+   |                ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:37:16
+   |
+LL |     let _ = s1.find(&s2[2..]).is_some();
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:39:21
+   |
+LL |     let _ = s1[2..].find("world").is_some();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:40:21
+   |
+LL |     let _ = s1[2..].find(&s2).is_some();
+   |                     ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:41:21
+   |
+LL |     let _ = s1[2..].find(&s2[2..]).is_some();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:57:44
+   |
+LL |             .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some())
+   |                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == &cc)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:73:49
+   |
+LL |             .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some())
+   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == cc)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:84:29
+   |
+LL |         let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.foo == 1 && v.bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:89:14
+   |
+LL |               .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+   |  ______________^
+LL | |             .is_some();
+   | |______________________^ help: use `any()` instead: `any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:95:29
+   |
+LL |         let _ = vfoo.iter().find(|a| a[0] == 42).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|a| a[0] == 42)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:101:29
+   |
+LL |         let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|sub| sub[1..4].len() == 3)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:119:30
+   |
+LL |         let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some();
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|ppp_x: &&u32| please(ppp_x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:120:50
+   |
+LL |         let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some();
+   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s.len() == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:123:26
+   |
+LL |         let _ = v.iter().find(|x| deref_enough(**x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| deref_enough(*x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:124:26
+   |
+LL |         let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| deref_enough(*x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:127:26
+   |
+LL |         let _ = v.iter().find(|x| arg_no_deref(x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| arg_no_deref(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:129:26
+   |
+LL |         let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:151:14
+   |
+LL |               .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+   |  ______________^
+LL | |             .is_some();
+   | |______________________^ help: use `any()` instead: `any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:165:29
+   |
+LL |         let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.inner[0].bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:170:29
+   |
+LL |         let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| (**x)[0] == 9)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:183:29
+   |
+LL |         let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.by_ref(&v.bar))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:187:55
+   |
+LL |         let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some();
+   |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:188:55
+   |
+LL |         let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
+   |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)`
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+  --> $DIR/search_is_some_fixable_some.rs:191:25
+   |
+LL |     fn test_string_1(s: &String) -> bool {
+   |                         ^^^^^^^ help: change this to: `&str`
+   |
+   = note: `-D clippy::ptr-arg` implied by `-D warnings`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:207:26
+   |
+LL |         let _ = v.iter().find(|s| s[0].is_empty()).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s[0].is_empty())`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:208:26
+   |
+LL |         let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| test_string_1(&s[0]))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:217:26
+   |
+LL |         let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| fp.field.is_power_of_two())`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:218:26
+   |
+LL |         let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_1(fp.field))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:219:26
+   |
+LL |         let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_2(*fp.field))`
+
+error: aborting due to 44 previous errors
+
index 7a45f1b18d4af2c8d920676b4f96b755083867d1..91916e7480fe14a57707bab2225b3ca10cc6713b 100644 (file)
@@ -1,6 +1,7 @@
 #![warn(clippy::semicolon_if_nothing_returned)]
 #![allow(clippy::redundant_closure)]
 #![feature(label_break_value)]
+#![feature(let_else)]
 
 fn get_unit() {}
 
@@ -110,3 +111,12 @@ macro_rules! repro {
     }
     repro!();
 }
+
+fn function_returning_option() -> Option<i32> {
+    Some(1)
+}
+
+// No warning
+fn let_else_stmts() {
+    let Some(x) = function_returning_option() else { return; };
+}
index 78813e7cc1c39c5aa294f08db14ca9cfe0688a50..41d2c1cfb87ae38ddda00a1cf2d4eaea3021a1c4 100644 (file)
@@ -1,5 +1,5 @@
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:9:5
+  --> $DIR/semicolon_if_nothing_returned.rs:10:5
    |
 LL |     println!("Hello")
    |     ^^^^^^^^^^^^^^^^^ help: add a `;` here: `println!("Hello");`
@@ -7,25 +7,25 @@ LL |     println!("Hello")
    = note: `-D clippy::semicolon-if-nothing-returned` implied by `-D warnings`
 
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:13:5
+  --> $DIR/semicolon_if_nothing_returned.rs:14:5
    |
 LL |     get_unit()
    |     ^^^^^^^^^^ help: add a `;` here: `get_unit();`
 
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:18:5
+  --> $DIR/semicolon_if_nothing_returned.rs:19:5
    |
 LL |     y = x + 1
    |     ^^^^^^^^^ help: add a `;` here: `y = x + 1;`
 
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:24:9
+  --> $DIR/semicolon_if_nothing_returned.rs:25:9
    |
 LL |         hello()
    |         ^^^^^^^ help: add a `;` here: `hello();`
 
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:35:9
+  --> $DIR/semicolon_if_nothing_returned.rs:36:9
    |
 LL |         ptr::drop_in_place(s.as_mut_ptr())
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());`
index 55caef59f7f683f5509412a2065391fb211f4c03..06f6949b66f5abbd50a2b59efea216d6d9e1413f 100644 (file)
@@ -79,4 +79,10 @@ fn question_mark() -> Option<()> {
     None
 }
 
+pub async fn foo1(_a: i32) {}
+
+pub async fn foo2(_a: i32, _b: i64) {
+    let _b = _a;
+}
+
 fn main() {}
index feed6e1ba8b8660ff2f73dc71a956fb135c62235..dcc7d4e6b2ff90adf0c8e463d4cbb0d285deafc0 100644 (file)
@@ -241,5 +241,17 @@ note: previous binding is here
 LL |     let _ = |[x]: [u32; 1]| {
    |               ^
 
-error: aborting due to 20 previous errors
+error: `_b` shadows a previous, unrelated binding
+  --> $DIR/shadow.rs:85:9
+   |
+LL |     let _b = _a;
+   |         ^^
+   |
+note: previous binding is here
+  --> $DIR/shadow.rs:84:28
+   |
+LL | pub async fn foo2(_a: i32, _b: i64) {
+   |                            ^^
+
+error: aborting due to 21 previous errors
 
index 1abd2b7883df0bd06f0fd5e456c4f29c6d350060..68e26726724b808425a6b53754bfa1d0047d4027 100644 (file)
@@ -17,6 +17,7 @@ fn main() {
     x.split('💣');
     // Can't use this lint for unicode code points which don't fit in a char
     x.split("❤️");
+    x.split_inclusive('x');
     x.contains('x');
     x.starts_with('x');
     x.ends_with('x');
@@ -27,6 +28,8 @@ fn main() {
     x.rsplit_terminator('x');
     x.splitn(2, 'x');
     x.rsplitn(2, 'x');
+    x.split_once('x');
+    x.rsplit_once('x');
     x.matches('x');
     x.rmatches('x');
     x.match_indices('x');
@@ -35,6 +38,8 @@ fn main() {
     x.trim_end_matches('x');
     x.strip_prefix('x');
     x.strip_suffix('x');
+    x.replace('x', "y");
+    x.replacen('x', "y", 3);
     // Make sure we escape characters correctly.
     x.split('\n');
     x.split('\'');
@@ -43,7 +48,7 @@ fn main() {
     let h = HashSet::<String>::new();
     h.contains("X"); // should not warn
 
-    x.replace(";", ",").split(','); // issue #2978
+    x.replace(';', ",").split(','); // issue #2978
     x.starts_with('\x03'); // issue #2996
 
     // Issue #3204
@@ -56,4 +61,7 @@ fn main() {
     x.split('a');
     x.split('\'');
     x.split('#');
+    // Must escape backslash in raw strings when converting to char #8060
+    x.split('\\');
+    x.split('\\');
 }
index e662bf34be2ceffb5ba409fe03613bbdb3710494..186202d78ec5aeae404064521c87393bb897857b 100644 (file)
@@ -17,6 +17,7 @@ fn main() {
     x.split("💣");
     // Can't use this lint for unicode code points which don't fit in a char
     x.split("❤️");
+    x.split_inclusive("x");
     x.contains("x");
     x.starts_with("x");
     x.ends_with("x");
@@ -27,6 +28,8 @@ fn main() {
     x.rsplit_terminator("x");
     x.splitn(2, "x");
     x.rsplitn(2, "x");
+    x.split_once("x");
+    x.rsplit_once("x");
     x.matches("x");
     x.rmatches("x");
     x.match_indices("x");
@@ -35,6 +38,8 @@ fn main() {
     x.trim_end_matches("x");
     x.strip_prefix("x");
     x.strip_suffix("x");
+    x.replace("x", "y");
+    x.replacen("x", "y", 3);
     // Make sure we escape characters correctly.
     x.split("\n");
     x.split("'");
@@ -43,7 +48,7 @@ fn main() {
     let h = HashSet::<String>::new();
     h.contains("X"); // should not warn
 
-    x.replace(";", ",").split(","); // issue #2978
+    x.replace(';', ",").split(","); // issue #2978
     x.starts_with("\x03"); // issue #2996
 
     // Issue #3204
@@ -56,4 +61,7 @@ fn main() {
     x.split(r###"a"###);
     x.split(r###"'"###);
     x.split(r###"#"###);
+    // Must escape backslash in raw strings when converting to char #8060
+    x.split(r#"\"#);
+    x.split(r"\");
 }
index 22d4b2d460fb047e217091ac1ff741c4a20e93bf..5564aac674d97624d1655857035c88163df88fd0 100644 (file)
@@ -25,172 +25,214 @@ LL |     x.split("💣");
    |             ^^^^ help: try using a `char` instead: `'💣'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:20:16
+  --> $DIR/single_char_pattern.rs:20:23
+   |
+LL |     x.split_inclusive("x");
+   |                       ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:21:16
    |
 LL |     x.contains("x");
    |                ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:21:19
+  --> $DIR/single_char_pattern.rs:22:19
    |
 LL |     x.starts_with("x");
    |                   ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:22:17
+  --> $DIR/single_char_pattern.rs:23:17
    |
 LL |     x.ends_with("x");
    |                 ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:23:12
+  --> $DIR/single_char_pattern.rs:24:12
    |
 LL |     x.find("x");
    |            ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:24:13
+  --> $DIR/single_char_pattern.rs:25:13
    |
 LL |     x.rfind("x");
    |             ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:25:14
+  --> $DIR/single_char_pattern.rs:26:14
    |
 LL |     x.rsplit("x");
    |              ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:26:24
+  --> $DIR/single_char_pattern.rs:27:24
    |
 LL |     x.split_terminator("x");
    |                        ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:27:25
+  --> $DIR/single_char_pattern.rs:28:25
    |
 LL |     x.rsplit_terminator("x");
    |                         ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:28:17
+  --> $DIR/single_char_pattern.rs:29:17
    |
 LL |     x.splitn(2, "x");
    |                 ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:29:18
+  --> $DIR/single_char_pattern.rs:30:18
    |
 LL |     x.rsplitn(2, "x");
    |                  ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:30:15
+  --> $DIR/single_char_pattern.rs:31:18
+   |
+LL |     x.split_once("x");
+   |                  ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:32:19
+   |
+LL |     x.rsplit_once("x");
+   |                   ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:33:15
    |
 LL |     x.matches("x");
    |               ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:31:16
+  --> $DIR/single_char_pattern.rs:34:16
    |
 LL |     x.rmatches("x");
    |                ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:32:21
+  --> $DIR/single_char_pattern.rs:35:21
    |
 LL |     x.match_indices("x");
    |                     ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:33:22
+  --> $DIR/single_char_pattern.rs:36:22
    |
 LL |     x.rmatch_indices("x");
    |                      ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:34:26
+  --> $DIR/single_char_pattern.rs:37:26
    |
 LL |     x.trim_start_matches("x");
    |                          ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:35:24
+  --> $DIR/single_char_pattern.rs:38:24
    |
 LL |     x.trim_end_matches("x");
    |                        ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:36:20
+  --> $DIR/single_char_pattern.rs:39:20
    |
 LL |     x.strip_prefix("x");
    |                    ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:37:20
+  --> $DIR/single_char_pattern.rs:40:20
    |
 LL |     x.strip_suffix("x");
    |                    ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:39:13
+  --> $DIR/single_char_pattern.rs:41:15
+   |
+LL |     x.replace("x", "y");
+   |               ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:42:16
+   |
+LL |     x.replacen("x", "y", 3);
+   |                ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:44:13
    |
 LL |     x.split("/n");
    |             ^^^^ help: try using a `char` instead: `'/n'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:40:13
+  --> $DIR/single_char_pattern.rs:45:13
    |
 LL |     x.split("'");
    |             ^^^ help: try using a `char` instead: `'/''`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:41:13
+  --> $DIR/single_char_pattern.rs:46:13
    |
 LL |     x.split("/'");
    |             ^^^^ help: try using a `char` instead: `'/''`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:46:31
+  --> $DIR/single_char_pattern.rs:51:31
    |
-LL |     x.replace(";", ",").split(","); // issue #2978
+LL |     x.replace(';', ",").split(","); // issue #2978
    |                               ^^^ help: try using a `char` instead: `','`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:47:19
+  --> $DIR/single_char_pattern.rs:52:19
    |
 LL |     x.starts_with("/x03"); // issue #2996
    |                   ^^^^^^ help: try using a `char` instead: `'/x03'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:54:13
+  --> $DIR/single_char_pattern.rs:59:13
    |
 LL |     x.split(r"a");
    |             ^^^^ help: try using a `char` instead: `'a'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:55:13
+  --> $DIR/single_char_pattern.rs:60:13
    |
 LL |     x.split(r#"a"#);
    |             ^^^^^^ help: try using a `char` instead: `'a'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:56:13
+  --> $DIR/single_char_pattern.rs:61:13
    |
 LL |     x.split(r###"a"###);
    |             ^^^^^^^^^^ help: try using a `char` instead: `'a'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:57:13
+  --> $DIR/single_char_pattern.rs:62:13
    |
 LL |     x.split(r###"'"###);
    |             ^^^^^^^^^^ help: try using a `char` instead: `'/''`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:58:13
+  --> $DIR/single_char_pattern.rs:63:13
    |
 LL |     x.split(r###"#"###);
    |             ^^^^^^^^^^ help: try using a `char` instead: `'#'`
 
-error: aborting due to 32 previous errors
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:65:13
+   |
+LL |     x.split(r#"/"#);
+   |             ^^^^^^ help: try using a `char` instead: `'/'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:66:13
+   |
+LL |     x.split(r"/");
+   |             ^^^^ help: try using a `char` instead: `'/'`
+
+error: aborting due to 39 previous errors
 
diff --git a/src/tools/clippy/tests/ui/strlen_on_c_strings.fixed b/src/tools/clippy/tests/ui/strlen_on_c_strings.fixed
new file mode 100644 (file)
index 0000000..947a59b
--- /dev/null
@@ -0,0 +1,34 @@
+// run-rustfix
+
+#![warn(clippy::strlen_on_c_strings)]
+#![allow(dead_code)]
+#![feature(rustc_private)]
+extern crate libc;
+
+#[allow(unused)]
+use libc::strlen;
+use std::ffi::{CStr, CString};
+
+fn main() {
+    // CString
+    let cstring = CString::new("foo").expect("CString::new failed");
+    let _ = cstring.as_bytes().len();
+
+    // CStr
+    let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
+    let _ = cstr.to_bytes().len();
+
+    let _ = cstr.to_bytes().len();
+
+    let pcstr: *const &CStr = &cstr;
+    let _ = unsafe { (*pcstr).to_bytes().len() };
+
+    unsafe fn unsafe_identity<T>(x: T) -> T {
+        x
+    }
+    let _ = unsafe { unsafe_identity(cstr).to_bytes().len() };
+    let _ = unsafe { unsafe_identity(cstr) }.to_bytes().len();
+
+    let f: unsafe fn(_) -> _ = unsafe_identity;
+    let _ = unsafe { f(cstr).to_bytes().len() };
+}
index 21902fa8483f3e9913fedd1572daa511fee13477..1237f1ab03acd07ebe1f5037fd28eb07fddb7048 100644 (file)
@@ -1,16 +1,34 @@
+// run-rustfix
+
 #![warn(clippy::strlen_on_c_strings)]
 #![allow(dead_code)]
 #![feature(rustc_private)]
 extern crate libc;
 
+#[allow(unused)]
+use libc::strlen;
 use std::ffi::{CStr, CString};
 
 fn main() {
     // CString
     let cstring = CString::new("foo").expect("CString::new failed");
-    let len = unsafe { libc::strlen(cstring.as_ptr()) };
+    let _ = unsafe { libc::strlen(cstring.as_ptr()) };
 
     // CStr
     let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
-    let len = unsafe { libc::strlen(cstr.as_ptr()) };
+    let _ = unsafe { libc::strlen(cstr.as_ptr()) };
+
+    let _ = unsafe { strlen(cstr.as_ptr()) };
+
+    let pcstr: *const &CStr = &cstr;
+    let _ = unsafe { strlen((*pcstr).as_ptr()) };
+
+    unsafe fn unsafe_identity<T>(x: T) -> T {
+        x
+    }
+    let _ = unsafe { strlen(unsafe_identity(cstr).as_ptr()) };
+    let _ = unsafe { strlen(unsafe { unsafe_identity(cstr) }.as_ptr()) };
+
+    let f: unsafe fn(_) -> _ = unsafe_identity;
+    let _ = unsafe { strlen(f(cstr).as_ptr()) };
 }
index e0ca511557c5b594981270097a7161909b6fb319..296268a5f1df79988635dd896c9715e3215cb9c9 100644 (file)
@@ -1,25 +1,46 @@
 error: using `libc::strlen` on a `CString` or `CStr` value
-  --> $DIR/strlen_on_c_strings.rs:11:24
+  --> $DIR/strlen_on_c_strings.rs:15:13
    |
-LL |     let len = unsafe { libc::strlen(cstring.as_ptr()) };
-   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let _ = unsafe { libc::strlen(cstring.as_ptr()) };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstring.as_bytes().len()`
    |
    = note: `-D clippy::strlen-on-c-strings` implied by `-D warnings`
-help: try this (you might also need to get rid of `unsafe` block in some cases):
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:19:13
+   |
+LL |     let _ = unsafe { libc::strlen(cstr.as_ptr()) };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstr.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:21:13
+   |
+LL |     let _ = unsafe { strlen(cstr.as_ptr()) };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstr.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:24:22
    |
-LL |     let len = unsafe { cstring.as_bytes().len() };
-   |                        ~~~~~~~~~~~~~~~~~~~~~~~~
+LL |     let _ = unsafe { strlen((*pcstr).as_ptr()) };
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*pcstr).to_bytes().len()`
 
 error: using `libc::strlen` on a `CString` or `CStr` value
-  --> $DIR/strlen_on_c_strings.rs:15:24
+  --> $DIR/strlen_on_c_strings.rs:29:22
    |
-LL |     let len = unsafe { libc::strlen(cstr.as_ptr()) };
-   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let _ = unsafe { strlen(unsafe_identity(cstr).as_ptr()) };
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unsafe_identity(cstr).to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:30:13
    |
-help: try this (you might also need to get rid of `unsafe` block in some cases):
+LL |     let _ = unsafe { strlen(unsafe { unsafe_identity(cstr) }.as_ptr()) };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unsafe { unsafe_identity(cstr) }.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:33:22
    |
-LL |     let len = unsafe { cstr.to_bytes().len() };
-   |                        ~~~~~~~~~~~~~~~~~~~~~
+LL |     let _ = unsafe { strlen(f(cstr).as_ptr()) };
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `f(cstr).to_bytes().len()`
 
-error: aborting due to 2 previous errors
+error: aborting due to 7 previous errors
 
index a21d94cf20bb1238195c5ffd451b68200d7da0dd..528f2ddcc862496a45c29c353a8e9c5f4e77ba7e 100644 (file)
@@ -1,4 +1,5 @@
 #![warn(clippy::suspicious_splitn)]
+#![allow(clippy::needless_splitn)]
 
 fn main() {
     let _ = "a,b,c".splitn(3, ',');
index b6220ae239339fbd0c162c03d0c9f905facbab89..3bcd681fa49d8af347654a367074a41cea139b40 100644 (file)
@@ -1,5 +1,5 @@
 error: `splitn` called with `0` splits
-  --> $DIR/suspicious_splitn.rs:9:13
+  --> $DIR/suspicious_splitn.rs:10:13
    |
 LL |     let _ = "a,b".splitn(0, ',');
    |             ^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     let _ = "a,b".splitn(0, ',');
    = note: the resulting iterator will always return `None`
 
 error: `rsplitn` called with `0` splits
-  --> $DIR/suspicious_splitn.rs:10:13
+  --> $DIR/suspicious_splitn.rs:11:13
    |
 LL |     let _ = "a,b".rsplitn(0, ',');
    |             ^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL |     let _ = "a,b".rsplitn(0, ',');
    = note: the resulting iterator will always return `None`
 
 error: `splitn` called with `1` split
-  --> $DIR/suspicious_splitn.rs:11:13
+  --> $DIR/suspicious_splitn.rs:12:13
    |
 LL |     let _ = "a,b".splitn(1, ',');
    |             ^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL |     let _ = "a,b".splitn(1, ',');
    = note: the resulting iterator will always return the entire string followed by `None`
 
 error: `splitn` called with `0` splits
-  --> $DIR/suspicious_splitn.rs:12:13
+  --> $DIR/suspicious_splitn.rs:13:13
    |
 LL |     let _ = [0, 1, 2].splitn(0, |&x| x == 1);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -32,7 +32,7 @@ LL |     let _ = [0, 1, 2].splitn(0, |&x| x == 1);
    = note: the resulting iterator will always return `None`
 
 error: `splitn_mut` called with `0` splits
-  --> $DIR/suspicious_splitn.rs:13:13
+  --> $DIR/suspicious_splitn.rs:14:13
    |
 LL |     let _ = [0, 1, 2].splitn_mut(0, |&x| x == 1);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL |     let _ = [0, 1, 2].splitn_mut(0, |&x| x == 1);
    = note: the resulting iterator will always return `None`
 
 error: `splitn` called with `1` split
-  --> $DIR/suspicious_splitn.rs:14:13
+  --> $DIR/suspicious_splitn.rs:15:13
    |
 LL |     let _ = [0, 1, 2].splitn(1, |&x| x == 1);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -48,7 +48,7 @@ LL |     let _ = [0, 1, 2].splitn(1, |&x| x == 1);
    = note: the resulting iterator will always return the entire slice followed by `None`
 
 error: `rsplitn_mut` called with `1` split
-  --> $DIR/suspicious_splitn.rs:15:13
+  --> $DIR/suspicious_splitn.rs:16:13
    |
 LL |     let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL |     let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1);
    = note: the resulting iterator will always return the entire slice followed by `None`
 
 error: `splitn` called with `1` split
-  --> $DIR/suspicious_splitn.rs:18:13
+  --> $DIR/suspicious_splitn.rs:19:13
    |
 LL |     let _ = "a,b".splitn(X + 1, ',');
    |             ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -64,7 +64,7 @@ LL |     let _ = "a,b".splitn(X + 1, ',');
    = note: the resulting iterator will always return the entire string followed by `None`
 
 error: `splitn` called with `0` splits
-  --> $DIR/suspicious_splitn.rs:19:13
+  --> $DIR/suspicious_splitn.rs:20:13
    |
 LL |     let _ = "a,b".splitn(X, ',');
    |             ^^^^^^^^^^^^^^^^^^^^
index 52577323a5837255f37698f6ff8076a941bd205c..7e510d8947522e3bf871893e6cca28a9e60845a0 100644 (file)
@@ -284,4 +284,8 @@ fn interference() {
     unsafe {};
 }
 
+pub fn print_binary_tree() {
+    println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
+}
+
 fn main() {}
index 613e9ffca456559a9352d85166f44711d7c7b787..ebe589001a1fe563bf320a94cb08ac12b6978719 100644 (file)
@@ -155,5 +155,17 @@ LL ~     // Safety: ...
 LL ~     unsafe {};
    |
 
-error: aborting due to 13 previous errors
+error: unsafe block missing a safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:288:20
+   |
+LL |     println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider adding a safety comment
+   |
+LL ~     println!("{}", // Safety: ...
+LL ~     unsafe { String::from_utf8_unchecked(vec![]) });
+   |
+
+error: aborting due to 14 previous errors
 
index 1f596c312fe395783208b6ab34ffd8fab27d8204..e0a4eadce33bd109bfd67f1ac8212f363a896811 100644 (file)
@@ -20,8 +20,16 @@ fn uni() {
     print!("\u{DC}ben!"); // this is ok
 }
 
+// issue 8013
+#[warn(clippy::non_ascii_literal)]
+fn single_quote() {
+    const _EMPTY_BLOCK: char = '▱';
+    const _FULL_BLOCK: char = '▰';
+}
+
 fn main() {
     zero();
     uni();
     canon();
+    single_quote();
 }
index 3fca463c620b522659335917cf78e0a2a5baeb24..3f54e3880e7470a46c61d2b1b0b422eb8b0d604b 100644 (file)
@@ -34,5 +34,17 @@ LL |     print!("Üben!");
    |
    = note: `-D clippy::non-ascii-literal` implied by `-D warnings`
 
-error: aborting due to 5 previous errors
+error: literal non-ASCII character detected
+  --> $DIR/unicode.rs:26:32
+   |
+LL |     const _EMPTY_BLOCK: char = '▱';
+   |                                ^^^ help: consider replacing the string with: `'/u{25b1}'`
+
+error: literal non-ASCII character detected
+  --> $DIR/unicode.rs:27:31
+   |
+LL |     const _FULL_BLOCK: char = '▰';
+   |                               ^^^ help: consider replacing the string with: `'/u{25b0}'`
+
+error: aborting due to 7 previous errors
 
index b0a13f827d6cd9564291aef82567c2ad7e53748e..80c30393832c107b93b6c45b9033b3dbb9fd0799 100644 (file)
@@ -5,3 +5,8 @@ allow-unauthenticated = [
 ]
 
 [assign]
+
+# Allows shortcuts like `@rustbot ready`
+#
+# See https://github.com/rust-lang/triagebot/wiki/Shortcuts
+[shortcut]
index 48421150a549fa4db6fc01dff272331a980b1a27..f175700a3f479dcf912db12fcb587ed44ea3d94b 100644 (file)
@@ -118,6 +118,12 @@ Otherwise, have a great day =^.^=
             background-color: #777777;
             margin: auto 5px;
         }
+
+        .label-version {
+            background-color: #777777;
+            margin: auto 5px;
+            font-family: monospace;
+        }
     </style>
     <style>
         /* Expanding the mdBoom theme*/
@@ -330,7 +336,7 @@ Otherwise, have a great day =^.^=
                     </h2>
                 </header>
 
-                <ul class="list-group lint-docs" ng-class="{collapse: true, in: open[lint.id]}">
+                <div class="list-group lint-docs" ng-class="{collapse: true, in: open[lint.id]}">
                     <div class="list-group-item lint-doc-md" ng-bind-html="lint.docs | markdown"></div>
                     <div class="lint-additional-info-container">
                         <!-- Applicability -->
@@ -339,7 +345,11 @@ Otherwise, have a great day =^.^=
                             <span class="label label-default label-applicability">{{lint.applicability.applicability}}</span>
                             <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a>
                         </div>
-                        <!-- TODO xFrednet 2021-05-19: Somehow collect and show the version See rust-clippy#6492 -->
+                        <!-- Clippy version -->
+                        <div class="lint-additional-info-item">
+                            <span>{{lint.group == "deprecated" ? "Deprecated" : "Added"}} in: </span>
+                            <span class="label label-default label-version">{{lint.version}}</span>
+                        </div>
                         <!-- Open related issues -->
                         <div class="lint-additional-info-item">
                             <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a>
@@ -349,7 +359,7 @@ Otherwise, have a great day =^.^=
                             <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/clippy_lints/{{lint.id_span.path}}#L{{lint.id_span.line}}">View Source</a>
                         </div>
                     </div>
-                </ul>
+                </div>
             </article>
         </div>
     </div>
index 4bf74c1508bd295882d648c39c8167ebd12987e1..3ccd4a1cc599a9a867d10964e11c5b5e640d0fc4 100644 (file)
@@ -1802,6 +1802,7 @@ fn make_compile_args(
                 // patterns still match the raw compiler output.
                 if self.props.error_patterns.is_empty() {
                     rustc.args(&["--error-format", "json"]);
+                    rustc.args(&["--json", "future-incompat"]);
                 }
                 rustc.arg("-Zui-testing");
                 rustc.arg("-Zdeduplicate-diagnostics=no");
@@ -1809,11 +1810,11 @@ fn make_compile_args(
             Ui => {
                 if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) {
                     rustc.args(&["--error-format", "json"]);
+                    rustc.args(&["--json", "future-incompat"]);
                 }
                 rustc.arg("-Ccodegen-units=1");
                 rustc.arg("-Zui-testing");
                 rustc.arg("-Zdeduplicate-diagnostics=no");
-                rustc.arg("-Zemit-future-incompat-report");
             }
             MirOpt => {
                 rustc.args(&[
index 9983e0fc634e11717eb457a73fe84cfc9409ba8f..81e59e6b92cf1729aabbbbf09b81a81a03775d64 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 9983e0fc634e11717eb457a73fe84cfc9409ba8f
+Subproject commit 81e59e6b92cf1729aabbbbf09b81a81a03775d64
index d9b2291f546abc77d24499339a72a89127464b95..7d6fcbc0be2151bfa85ec146545b42d8be2fb28c 160000 (submodule)
@@ -1 +1 @@
-Subproject commit d9b2291f546abc77d24499339a72a89127464b95
+Subproject commit 7d6fcbc0be2151bfa85ec146545b42d8be2fb28c
index de1327d74ff57f8d567b9dac4259dca434d52f57..b8c67de26230b076666647e808401645e9174931 100644 (file)
@@ -9,6 +9,6 @@ clap = "2.25.0"
 env_logger = "0.7.1"
 
 [dependencies.mdbook]
-version = "0.4.12"
+version = "0.4.14"
 default-features = false
 features = ["search"]
index 0d6a2605d4f2aabb09a0f76635c6d918de5569ec..3feb4940d87f837d27422e478eab9044509fdfec 100644 (file)
@@ -172,11 +172,16 @@ async function main(argv) {
     }
     files.sort();
 
+    if (no_headless) {
+        opts["jobs"] = 1;
+        console.log("`--no-headless` option is active, disabling concurrency for running tests.");
+    }
+
     console.log(`Running ${files.length} rustdoc-gui (${opts["jobs"]} concurrently) ...`);
 
     if (opts["jobs"] < 1) {
         process.setMaxListeners(files.length + 1);
-    } else {
+    } else if (!no_headless) {
         process.setMaxListeners(opts["jobs"] + 1);
     }
 
@@ -217,9 +222,7 @@ async function main(argv) {
                 tests_queue.splice(tests_queue.indexOf(callback), 1);
             });
         tests_queue.push(callback);
-        if (no_headless) {
-            await tests_queue[i];
-        } else if (opts["jobs"] > 0 && tests_queue.length >= opts["jobs"]) {
+        if (opts["jobs"] > 0 && tests_queue.length >= opts["jobs"]) {
             await Promise.race(tests_queue);
         }
     }