]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #81458 - estebank:match-stmt-remove-semi, r=oli-obk
authorbors <bors@rust-lang.org>
Fri, 26 Feb 2021 12:03:38 +0000 (12:03 +0000)
committerbors <bors@rust-lang.org>
Fri, 26 Feb 2021 12:03:38 +0000 (12:03 +0000)
Detect match statement intended to be tail expression

CC #24157

251 files changed:
Cargo.lock
compiler/rustc_ast/src/ast.rs
compiler/rustc_ast_lowering/src/expr.rs
compiler/rustc_ast_lowering/src/lib.rs
compiler/rustc_builtin_macros/src/global_asm.rs
compiler/rustc_builtin_macros/src/standard_library_imports.rs
compiler/rustc_codegen_llvm/src/allocator.rs
compiler/rustc_codegen_llvm/src/asm.rs
compiler/rustc_codegen_ssa/src/back/link.rs
compiler/rustc_driver/src/pretty.rs
compiler/rustc_error_codes/src/error_codes/E0761.md
compiler/rustc_errors/src/diagnostic.rs
compiler/rustc_feature/src/builtin_attrs.rs
compiler/rustc_hir/src/hir.rs
compiler/rustc_interface/src/passes.rs
compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
compiler/rustc_middle/src/lint.rs
compiler/rustc_middle/src/mir/interpret/allocation.rs
compiler/rustc_middle/src/mir/interpret/error.rs
compiler/rustc_middle/src/query/mod.rs
compiler/rustc_middle/src/ty/layout.rs
compiler/rustc_middle/src/ty/sty.rs
compiler/rustc_mir/src/borrow_check/type_check/mod.rs
compiler/rustc_mir/src/dataflow/framework/mod.rs
compiler/rustc_mir/src/interpret/intern.rs
compiler/rustc_mir/src/transform/const_prop.rs
compiler/rustc_mir/src/transform/generator.rs
compiler/rustc_parse/src/parser/item.rs
compiler/rustc_passes/src/check_attr.rs
compiler/rustc_resolve/src/late.rs
compiler/rustc_resolve/src/late/diagnostics.rs
compiler/rustc_resolve/src/lib.rs
compiler/rustc_session/src/config.rs
compiler/rustc_span/src/symbol.rs
compiler/rustc_ty_utils/src/lib.rs
compiler/rustc_ty_utils/src/ty.rs
compiler/rustc_typeck/src/check/callee.rs
compiler/rustc_typeck/src/check/coercion.rs
compiler/rustc_typeck/src/check/demand.rs
compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
compiler/rustc_typeck/src/check/method/probe.rs
library/alloc/src/collections/btree/map/entry.rs
library/alloc/src/vec/mod.rs
library/core/src/char/methods.rs
library/core/src/intrinsics.rs
library/core/src/iter/adapters/flatten.rs
library/core/src/lib.rs
library/core/src/macros/mod.rs
library/core/src/num/error.rs
library/core/src/num/f32.rs
library/core/src/num/f64.rs
library/core/src/num/int_macros.rs
library/core/src/num/mod.rs
library/core/src/num/uint_macros.rs
library/core/src/option.rs
library/core/src/ptr/mod.rs
library/core/src/ptr/mut_ptr.rs
library/core/src/result.rs
library/core/tests/const_ptr.rs
library/core/tests/lib.rs
library/std/src/error.rs
library/std/src/io/copy.rs
library/std/src/lazy.rs
library/std/src/lib.rs
library/std/src/macros.rs
library/std/src/os/raw/mod.rs
library/std/src/sys/unix/ext/process.rs
library/std/src/sys/unix/process/process_common.rs
library/std/src/sys/windows/path.rs
library/std/src/sys_common/rwlock.rs
src/doc/unstable-book/src/compiler-flags/sanitizer.md
src/doc/unstable-book/src/language-features/lang-items.md
src/etc/pre-commit.sh
src/librustdoc/clean/inline.rs
src/librustdoc/clean/types.rs
src/librustdoc/html/markdown.rs
src/librustdoc/html/markdown/tests.rs
src/librustdoc/html/render/mod.rs
src/librustdoc/html/render/tests.rs
src/librustdoc/html/static/normalize.css
src/librustdoc/passes/collect_intra_doc_links.rs
src/test/ui/auxiliary/legacy-const-generics.rs [new file with mode: 0644]
src/test/ui/const-generics/conservative_is_privately_uninhabited_uses_correct_param_env-1.rs [new file with mode: 0644]
src/test/ui/const-generics/conservative_is_privately_uninhabited_uses_correct_param_env-2.rs [new file with mode: 0644]
src/test/ui/consts/const-as-fn.rs [new file with mode: 0644]
src/test/ui/consts/const-as-fn.stderr [new file with mode: 0644]
src/test/ui/error-codes/E0618.stderr
src/test/ui/inference/auxiliary/inference_unstable_iterator.rs
src/test/ui/inference/auxiliary/inference_unstable_itertools.rs
src/test/ui/inference/inference_unstable.rs
src/test/ui/inference/inference_unstable.stderr
src/test/ui/invalid-rustc_args_required_const-arguments.rs [deleted file]
src/test/ui/invalid-rustc_args_required_const-arguments.stderr [deleted file]
src/test/ui/invalid/invalid-rustc_args_required_const-arguments.rs [new file with mode: 0644]
src/test/ui/invalid/invalid-rustc_args_required_const-arguments.stderr [new file with mode: 0644]
src/test/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs [new file with mode: 0644]
src/test/ui/invalid/invalid-rustc_legacy_const_generics-arguments.stderr [new file with mode: 0644]
src/test/ui/issues/issue-10969.stderr
src/test/ui/issues/issue-21701.stderr
src/test/ui/issues/issue-22468.stderr
src/test/ui/issues/issue-26237.stderr
src/test/ui/legacy-const-generics-bad.rs [new file with mode: 0644]
src/test/ui/legacy-const-generics-bad.stderr [new file with mode: 0644]
src/test/ui/legacy-const-generics.rs [new file with mode: 0644]
src/test/ui/parser/parse-error-correct.stderr
src/test/ui/structs/80853.rs [new file with mode: 0644]
src/test/ui/structs/80853.stderr [new file with mode: 0644]
src/test/ui/suggestions/do-not-attempt-to-add-suggestions-with-no-changes.rs [new file with mode: 0644]
src/test/ui/suggestions/do-not-attempt-to-add-suggestions-with-no-changes.stderr [new file with mode: 0644]
src/test/ui/suggestions/issue-82361.fixed [new file with mode: 0644]
src/test/ui/suggestions/issue-82361.rs [new file with mode: 0644]
src/test/ui/suggestions/issue-82361.stderr [new file with mode: 0644]
src/test/ui/treat-err-as-bug/delay_span_bug.rs
src/test/ui/treat-err-as-bug/delay_span_bug.stderr
src/test/ui/treat-err-as-bug/err.rs
src/test/ui/treat-err-as-bug/err.stderr
src/test/ui/typeck/auxiliary/issue-81943-lib.rs [new file with mode: 0644]
src/test/ui/typeck/issue-81943.rs [new file with mode: 0644]
src/test/ui/typeck/issue-81943.stderr [new file with mode: 0644]
src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md
src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md
src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md
src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md
src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md
src/tools/clippy/.github/workflows/clippy.yml
src/tools/clippy/.github/workflows/clippy_bors.yml
src/tools/clippy/.gitignore
src/tools/clippy/CHANGELOG.md
src/tools/clippy/Cargo.toml
src/tools/clippy/README.md
src/tools/clippy/clippy_dev/Cargo.toml
src/tools/clippy/clippy_dev/README.md
src/tools/clippy/clippy_dev/src/bless.rs
src/tools/clippy/clippy_dev/src/lintcheck.rs
src/tools/clippy/clippy_dev/src/main.rs
src/tools/clippy/clippy_lints/Cargo.toml
src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.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/consts.rs
src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/doc.rs
src/tools/clippy/clippy_lints/src/from_str_radix_10.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/functions.rs
src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/inherent_to_string.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/manual_map.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/matches.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/upper_case_acronyms.rs
src/tools/clippy/clippy_lints/src/use_self.rs
src/tools/clippy/clippy_lints/src/utils/ast_utils.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/ast_utils/ident_iter.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/attrs.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/camel_case.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/comparisons.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/diagnostics.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/eager_or_lazy.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/higher.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/hir_utils.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/mod.rs
src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/paths.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/ptr.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/qualify_min_const_fn.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/sugg.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/sym_helper.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/usage.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/visitors.rs [deleted file]
src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
src/tools/clippy/clippy_lints/src/write.rs
src/tools/clippy/clippy_utils/Cargo.toml [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/ast_utils.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/attrs.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/camel_case.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/comparisons.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/consts.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/diagnostics.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/eager_or_lazy.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/higher.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/hir_utils.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/lib.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/numeric_literal.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/paths.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/ptr.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/sugg.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/sym_helper.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/usage.rs [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/visitors.rs [new file with mode: 0644]
src/tools/clippy/doc/adding_lints.md
src/tools/clippy/doc/common_tools_writing_lints.md
src/tools/clippy/lintcheck-logs/lintcheck_crates_logs.txt
src/tools/clippy/rust-toolchain
src/tools/clippy/tests/compile-test.rs
src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/clippy.toml [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
src/tools/clippy/tests/ui/blocks_in_if_conditions_closure.rs
src/tools/clippy/tests/ui/collapsible_else_if.fixed
src/tools/clippy/tests/ui/collapsible_else_if.rs
src/tools/clippy/tests/ui/collapsible_if.fixed
src/tools/clippy/tests/ui/collapsible_if.rs
src/tools/clippy/tests/ui/collapsible_match.rs
src/tools/clippy/tests/ui/collapsible_match.stderr
src/tools/clippy/tests/ui/collapsible_match2.stderr
src/tools/clippy/tests/ui/crashes/ice-6179.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/default_numeric_fallback.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/default_numeric_fallback.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/doc.rs
src/tools/clippy/tests/ui/doc.stderr
src/tools/clippy/tests/ui/doc_panics.rs
src/tools/clippy/tests/ui/doc_panics.stderr
src/tools/clippy/tests/ui/enum_variants.rs
src/tools/clippy/tests/ui/enum_variants.stderr
src/tools/clippy/tests/ui/from_str_radix_10.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/from_str_radix_10.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/if_same_then_else2.rs
src/tools/clippy/tests/ui/if_same_then_else2.stderr
src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/inherent_to_string.rs
src/tools/clippy/tests/ui/inherent_to_string.stderr
src/tools/clippy/tests/ui/manual_map_option.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/manual_map_option.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/manual_map_option.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/result_unit_error.rs
src/tools/clippy/tests/ui/result_unit_error.stderr
src/tools/clippy/tests/ui/unnecessary_wraps.rs
src/tools/clippy/tests/ui/unnecessary_wraps.stderr
src/tools/clippy/tests/ui/upper_case_acronyms.rs
src/tools/clippy/tests/ui/upper_case_acronyms.stderr
src/tools/clippy/tests/ui/use_self.fixed
src/tools/clippy/tests/ui/use_self.rs
src/tools/clippy/tests/ui/use_self.stderr
src/tools/clippy/tests/ui/use_self_trait.fixed
src/tools/clippy/tests/ui/use_self_trait.rs
src/tools/clippy/tests/ui/use_self_trait.stderr
src/tools/clippy/tests/ui/vec_init_then_push.rs
src/tools/clippy/tests/ui/vec_init_then_push.stderr
src/tools/clippy/tests/versioncheck.rs
src/tools/compiletest/src/main.rs
src/tools/tidy/src/style.rs

index c717a8f1a9d5ba7601d51d7f310b9dd3f67b0422..7491c1712669ca8c6ec5dde82e81977c7119cda3 100644 (file)
@@ -565,14 +565,14 @@ dependencies = [
  "cargo_metadata 0.12.0",
  "clippy-mini-macro-test",
  "clippy_lints",
- "compiletest_rs",
+ "compiletest_rs 0.6.0",
  "derive-new",
  "rustc-workspace-hack",
  "rustc_tools_util 0.2.0",
  "semver 0.11.0",
  "serde",
  "tempfile",
- "tester",
+ "tester 0.9.0",
 ]
 
 [[package]]
@@ -584,6 +584,7 @@ name = "clippy_lints"
 version = "0.1.52"
 dependencies = [
  "cargo_metadata 0.12.0",
+ "clippy_utils",
  "if_chain",
  "itertools 0.9.0",
  "pulldown-cmark 0.8.0",
@@ -600,6 +601,20 @@ dependencies = [
  "url 2.1.1",
 ]
 
+[[package]]
+name = "clippy_utils"
+version = "0.1.52"
+dependencies = [
+ "if_chain",
+ "itertools 0.9.0",
+ "regex-syntax",
+ "rustc-semver",
+ "serde",
+ "smallvec 1.6.1",
+ "toml",
+ "unicode-normalization",
+]
+
 [[package]]
 name = "cloudabi"
 version = "0.1.0"
@@ -695,7 +710,30 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "tempfile",
- "tester",
+ "tester 0.7.0",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "compiletest_rs"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0086d6ad78cf409c3061618cd98e2789d5c9ce598fc9651611cf62eae0a599cb"
+dependencies = [
+ "diff",
+ "filetime",
+ "getopts",
+ "lazy_static",
+ "libc",
+ "log",
+ "miow 0.3.6",
+ "regex",
+ "rustfix",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "tempfile",
+ "tester 0.9.0",
  "winapi 0.3.9",
 ]
 
@@ -984,6 +1022,16 @@ dependencies = [
  "dirs-sys",
 ]
 
+[[package]]
+name = "dirs-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+dependencies = [
+ "cfg-if 1.0.0",
+ "dirs-sys-next",
+]
+
 [[package]]
 name = "dirs-sys"
 version = "0.3.5"
@@ -991,7 +1039,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
 dependencies = [
  "libc",
- "redox_users",
+ "redox_users 0.3.4",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users 0.4.0",
  "winapi 0.3.9",
 ]
 
@@ -1115,7 +1174,7 @@ checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e"
 dependencies = [
  "cfg-if 0.1.10",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.1.57",
  "winapi 0.3.9",
 ]
 
@@ -2226,7 +2285,7 @@ name = "miri"
 version = "0.1.0"
 dependencies = [
  "colored",
- "compiletest_rs",
+ "compiletest_rs 0.5.0",
  "env_logger 0.7.1",
  "getrandom 0.2.0",
  "hex 0.4.2",
@@ -2458,7 +2517,7 @@ dependencies = [
  "cloudabi",
  "instant",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.1.57",
  "smallvec 1.6.1",
  "winapi 0.3.9",
 ]
@@ -2899,6 +2958,15 @@ version = "0.1.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
 
+[[package]]
+name = "redox_syscall"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
+dependencies = [
+ "bitflags",
+]
+
 [[package]]
 name = "redox_users"
 version = "0.3.4"
@@ -2906,10 +2974,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431"
 dependencies = [
  "getrandom 0.1.14",
- "redox_syscall",
+ "redox_syscall 0.1.57",
  "rust-argon2",
 ]
 
+[[package]]
+name = "redox_users"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
+dependencies = [
+ "getrandom 0.2.0",
+ "redox_syscall 0.2.5",
+]
+
 [[package]]
 name = "regex"
 version = "1.4.3"
@@ -4535,6 +4613,12 @@ dependencies = [
  "unicode_categories",
 ]
 
+[[package]]
+name = "rustversion"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd"
+
 [[package]]
 name = "ryu"
 version = "1.0.5"
@@ -4812,7 +4896,7 @@ checksum = "7fd8b795c389288baa5f355489c65e71fd48a02104600d15c4cfbc561e9e429d"
 dependencies = [
  "cfg-if 0.1.10",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.1.57",
  "winapi 0.3.9",
 ]
 
@@ -4973,7 +5057,7 @@ checksum = "c8a4c1d0bee3230179544336c15eefb563cf0302955d962e456542323e8c2e8a"
 dependencies = [
  "filetime",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.1.57",
  "xattr",
 ]
 
@@ -4986,7 +5070,7 @@ dependencies = [
  "cfg-if 0.1.10",
  "libc",
  "rand",
- "redox_syscall",
+ "redox_syscall 0.1.57",
  "remove_dir_all",
  "winapi 0.3.9",
 ]
@@ -5020,6 +5104,17 @@ dependencies = [
  "winapi 0.3.9",
 ]
 
+[[package]]
+name = "term"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
+dependencies = [
+ "dirs-next",
+ "rustversion",
+ "winapi 0.3.9",
+]
+
 [[package]]
 name = "termcolor"
 version = "1.1.0"
@@ -5065,6 +5160,19 @@ dependencies = [
  "term 0.6.1",
 ]
 
+[[package]]
+name = "tester"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0639d10d8f4615f223a57275cf40f9bdb7cfbb806bcb7f7cc56e3beb55a576eb"
+dependencies = [
+ "cfg-if 1.0.0",
+ "getopts",
+ "libc",
+ "num_cpus",
+ "term 0.7.0",
+]
+
 [[package]]
 name = "textwrap"
 version = "0.11.0"
index 1d2f1f694cf5be48ea42b633991331050e5bb69d..16b4378e7f77f5958c1068ac2a3d1cc106fe985a 100644 (file)
@@ -2695,7 +2695,7 @@ pub enum ItemKind {
     /// A use declaration item (`use`).
     ///
     /// E.g., `use foo;`, `use foo::bar;` or `use foo::bar as FooBar;`.
-    Use(P<UseTree>),
+    Use(UseTree),
     /// A static item (`static`).
     ///
     /// E.g., `static FOO: i32 = 42;` or `static FOO: &'static str = "bar";`.
@@ -2719,7 +2719,7 @@ pub enum ItemKind {
     /// E.g., `extern {}` or `extern "C" {}`.
     ForeignMod(ForeignMod),
     /// Module-level inline assembly (from `global_asm!()`).
-    GlobalAsm(P<GlobalAsm>),
+    GlobalAsm(GlobalAsm),
     /// A type alias (`type`).
     ///
     /// E.g., `type Foo = Bar<u8>;`.
index 6d44feec2c4969f6095b557048460a7313e74550..82b41e13cccff4bf307fa13221bb2e05d94fe330 100644 (file)
@@ -9,7 +9,9 @@
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
+use rustc_hir::definitions::DefPathData;
 use rustc_session::parse::feature_err;
+use rustc_span::hygiene::ExpnId;
 use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::{hygiene::ForLoopLoc, DUMMY_SP};
@@ -42,8 +44,12 @@ pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> {
                 }
                 ExprKind::Tup(ref elts) => hir::ExprKind::Tup(self.lower_exprs(elts)),
                 ExprKind::Call(ref f, ref args) => {
-                    let f = self.lower_expr(f);
-                    hir::ExprKind::Call(f, self.lower_exprs(args))
+                    if let Some(legacy_args) = self.resolver.legacy_const_generic_args(f) {
+                        self.lower_legacy_const_generics((**f).clone(), args.clone(), &legacy_args)
+                    } else {
+                        let f = self.lower_expr(f);
+                        hir::ExprKind::Call(f, self.lower_exprs(args))
+                    }
                 }
                 ExprKind::MethodCall(ref seg, ref args, span) => {
                     let hir_seg = self.arena.alloc(self.lower_path_segment(
@@ -292,6 +298,54 @@ fn lower_binop(&mut self, b: BinOp) -> hir::BinOp {
         }
     }
 
+    fn lower_legacy_const_generics(
+        &mut self,
+        mut f: Expr,
+        args: Vec<AstP<Expr>>,
+        legacy_args_idx: &[usize],
+    ) -> hir::ExprKind<'hir> {
+        let path = match f.kind {
+            ExprKind::Path(None, ref mut path) => path,
+            _ => unreachable!(),
+        };
+
+        // Split the arguments into const generics and normal arguments
+        let mut real_args = vec![];
+        let mut generic_args = vec![];
+        for (idx, arg) in args.into_iter().enumerate() {
+            if legacy_args_idx.contains(&idx) {
+                let parent_def_id = self.current_hir_id_owner.last().unwrap().0;
+                let node_id = self.resolver.next_node_id();
+
+                // Add a definition for the in-band const def.
+                self.resolver.create_def(
+                    parent_def_id,
+                    node_id,
+                    DefPathData::AnonConst,
+                    ExpnId::root(),
+                    arg.span,
+                );
+
+                let anon_const = AnonConst { id: node_id, value: arg };
+                generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const)));
+            } else {
+                real_args.push(arg);
+            }
+        }
+
+        // Add generic args to the last element of the path.
+        let last_segment = path.segments.last_mut().unwrap();
+        assert!(last_segment.args.is_none());
+        last_segment.args = Some(AstP(GenericArgs::AngleBracketed(AngleBracketedArgs {
+            span: DUMMY_SP,
+            args: generic_args,
+        })));
+
+        // Now lower everything as normal.
+        let f = self.lower_expr(&f);
+        hir::ExprKind::Call(f, self.lower_exprs(&real_args))
+    }
+
     /// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into:
     /// ```rust
     /// match scrutinee { pats => true, _ => false }
index 05b417effd491688e9782633fc90e3c3f4a6c706..c564ada0395e9cddab97acb422584a30194f1492 100644 (file)
@@ -175,6 +175,8 @@ pub trait ResolverAstLowering {
 
     fn item_generics_num_lifetimes(&self, def: DefId, sess: &Session) -> usize;
 
+    fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option<Vec<usize>>;
+
     /// Obtains resolution for a `NodeId` with a single resolution.
     fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes>;
 
index 3689e33be6f0f39b9bb7bea2a18bc42b9ed3665c..76d874529270e179167c66ce5a78fc0d9765e568 100644 (file)
@@ -28,7 +28,7 @@ pub fn expand_global_asm<'cx>(
             ident: Ident::invalid(),
             attrs: Vec::new(),
             id: ast::DUMMY_NODE_ID,
-            kind: ast::ItemKind::GlobalAsm(P(global_asm)),
+            kind: ast::ItemKind::GlobalAsm(global_asm),
             vis: ast::Visibility {
                 span: sp.shrink_to_lo(),
                 kind: ast::VisibilityKind::Inherited,
index 3a81d076dc52fdbd6ea2db324bdc90bee7cc40f0..f446e6be84ca4dbdf38e6ffa9916533b8b8d51ef 100644 (file)
@@ -1,5 +1,4 @@
 use rustc_ast as ast;
-use rustc_ast::ptr::P;
 use rustc_expand::base::{ExtCtxt, ResolverExpand};
 use rustc_expand::expand::ExpansionConfig;
 use rustc_session::Session;
@@ -72,11 +71,11 @@ pub fn inject(
         span,
         Ident::invalid(),
         vec![cx.attribute(cx.meta_word(span, sym::prelude_import))],
-        ast::ItemKind::Use(P(ast::UseTree {
+        ast::ItemKind::Use(ast::UseTree {
             prefix: cx.path(span, import_path),
             kind: ast::UseTreeKind::Glob,
             span,
-        })),
+        }),
     );
 
     krate.items.insert(0, use_item);
index a5ea0b2a74c5c90f98632f84951ffc5182fadf44..068e5e99efead3487e0b88137e4a024c88b7e520 100644 (file)
@@ -93,7 +93,7 @@ pub(crate) unsafe fn codegen(
     let args = [usize, usize]; // size, align
 
     let ty = llvm::LLVMFunctionType(void, args.as_ptr(), args.len() as c_uint, False);
-    let name = "__rust_alloc_error_handler".to_string();
+    let name = "__rust_alloc_error_handler";
     let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty);
     // -> ! DIFlagNoReturn
     llvm::Attribute::NoReturn.apply_llfn(llvm::AttributePlace::Function, llfn);
index 4aa25aae74760d03de57248d1abd8745c09ae0f4..26815de403fd25d8547828afb1ef55dc5b73de5d 100644 (file)
@@ -61,9 +61,9 @@ fn codegen_llvm_inline_asm(
         // Default per-arch clobbers
         // Basically what clang does
         let arch_clobbers = match &self.sess().target.arch[..] {
-            "x86" | "x86_64" => vec!["~{dirflag}", "~{fpsr}", "~{flags}"],
-            "mips" | "mips64" => vec!["~{$1}"],
-            _ => Vec::new(),
+            "x86" | "x86_64" => &["~{dirflag}", "~{fpsr}", "~{flags}"][..],
+            "mips" | "mips64" => &["~{$1}"],
+            _ => &[],
         };
 
         let all_constraints = ia
index b93a579df15269b5081b8f4dc004893f829aaafb..08932e26bf1c7d3b47934ec146a1f93eafb97cdd 100644 (file)
@@ -181,16 +181,16 @@ fn get_linker(
             let original_path = tool.path();
             if let Some(ref root_lib_path) = original_path.ancestors().nth(4) {
                 let arch = match t.arch.as_str() {
-                    "x86_64" => Some("x64".to_string()),
-                    "x86" => Some("x86".to_string()),
-                    "aarch64" => Some("arm64".to_string()),
-                    "arm" => Some("arm".to_string()),
+                    "x86_64" => Some("x64"),
+                    "x86" => Some("x86"),
+                    "aarch64" => Some("arm64"),
+                    "arm" => Some("arm"),
                     _ => None,
                 };
                 if let Some(ref a) = arch {
                     // FIXME: Move this to `fn linker_with_args`.
                     let mut arg = OsString::from("/LIBPATH:");
-                    arg.push(format!("{}\\lib\\{}\\store", root_lib_path.display(), a.to_string()));
+                    arg.push(format!("{}\\lib\\{}\\store", root_lib_path.display(), a));
                     cmd.arg(&arg);
                 } else {
                     warn!("arch is not supported");
index 17d2b3386f5febc5264658a7a983fc10cbfc1cf4..1dcc4d147acf23a45de96335b4c4b9fba070d67e 100644 (file)
@@ -9,7 +9,7 @@
 use rustc_middle::hir::map as hir_map;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_mir::util::{write_mir_graphviz, write_mir_pretty};
-use rustc_session::config::{Input, PpMode, PpSourceMode};
+use rustc_session::config::{Input, PpHirMode, PpMode, PpSourceMode};
 use rustc_session::Session;
 use rustc_span::symbol::Ident;
 use rustc_span::FileName;
@@ -42,43 +42,41 @@ fn call_with_pp_support<'tcx, A, F>(
     F: FnOnce(&dyn PrinterSupport) -> A,
 {
     match *ppmode {
-        PpmNormal | PpmEveryBodyLoops | PpmExpanded => {
+        Normal | EveryBodyLoops | Expanded => {
             let annotation = NoAnn { sess, tcx };
             f(&annotation)
         }
 
-        PpmIdentified | PpmExpandedIdentified => {
+        Identified | ExpandedIdentified => {
             let annotation = IdentifiedAnnotation { sess, tcx };
             f(&annotation)
         }
-        PpmExpandedHygiene => {
+        ExpandedHygiene => {
             let annotation = HygieneAnnotation { sess };
             f(&annotation)
         }
-        _ => panic!("Should use call_with_pp_support_hir"),
     }
 }
-fn call_with_pp_support_hir<A, F>(ppmode: &PpSourceMode, tcx: TyCtxt<'_>, f: F) -> A
+fn call_with_pp_support_hir<A, F>(ppmode: &PpHirMode, tcx: TyCtxt<'_>, f: F) -> A
 where
     F: FnOnce(&dyn HirPrinterSupport<'_>, &hir::Crate<'_>) -> A,
 {
     match *ppmode {
-        PpmNormal => {
+        PpHirMode::Normal => {
             let annotation = NoAnn { sess: tcx.sess, tcx: Some(tcx) };
             f(&annotation, tcx.hir().krate())
         }
 
-        PpmIdentified => {
+        PpHirMode::Identified => {
             let annotation = IdentifiedAnnotation { sess: tcx.sess, tcx: Some(tcx) };
             f(&annotation, tcx.hir().krate())
         }
-        PpmTyped => {
+        PpHirMode::Typed => {
             abort_on_err(tcx.analysis(LOCAL_CRATE), tcx.sess);
 
             let annotation = TypedAnnotation { tcx, maybe_typeck_results: Cell::new(None) };
             tcx.dep_graph.with_ignore(|| f(&annotation, tcx.hir().krate()))
         }
-        _ => panic!("Should use call_with_pp_support"),
     }
 }
 
@@ -393,16 +391,13 @@ pub fn print_after_parsing(
 ) {
     let (src, src_name) = get_source(input, sess);
 
-    let mut out = String::new();
-
-    if let PpmSource(s) = ppm {
+    let out = if let Source(s) = ppm {
         // Silently ignores an identified node.
-        let out = &mut out;
         call_with_pp_support(&s, sess, None, move |annotation| {
             debug!("pretty printing source code {:?}", s);
             let sess = annotation.sess();
             let parse = &sess.parse_sess;
-            *out = pprust::print_crate(
+            pprust::print_crate(
                 sess.source_map(),
                 krate,
                 src_name,
@@ -413,7 +408,7 @@ pub fn print_after_parsing(
             )
         })
     } else {
-        unreachable!();
+        unreachable!()
     };
 
     write_or_print(&out, ofile);
@@ -433,17 +428,14 @@ pub fn print_after_hir_lowering<'tcx>(
 
     let (src, src_name) = get_source(input, tcx.sess);
 
-    let mut out = String::new();
-
-    match ppm {
-        PpmSource(s) => {
+    let out = match ppm {
+        Source(s) => {
             // Silently ignores an identified node.
-            let out = &mut out;
             call_with_pp_support(&s, tcx.sess, Some(tcx), move |annotation| {
                 debug!("pretty printing source code {:?}", s);
                 let sess = annotation.sess();
                 let parse = &sess.parse_sess;
-                *out = pprust::print_crate(
+                pprust::print_crate(
                     sess.source_map(),
                     krate,
                     src_name,
@@ -455,26 +447,20 @@ pub fn print_after_hir_lowering<'tcx>(
             })
         }
 
-        PpmHir(s) => {
-            let out = &mut out;
-            call_with_pp_support_hir(&s, tcx, move |annotation, krate| {
-                debug!("pretty printing source code {:?}", s);
-                let sess = annotation.sess();
-                let sm = sess.source_map();
-                *out = pprust_hir::print_crate(sm, krate, src_name, src, annotation.pp_ann())
-            })
-        }
+        Hir(s) => call_with_pp_support_hir(&s, tcx, move |annotation, krate| {
+            debug!("pretty printing HIR {:?}", s);
+            let sess = annotation.sess();
+            let sm = sess.source_map();
+            pprust_hir::print_crate(sm, krate, src_name, src, annotation.pp_ann())
+        }),
 
-        PpmHirTree(s) => {
-            let out = &mut out;
-            call_with_pp_support_hir(&s, tcx, move |_annotation, krate| {
-                debug!("pretty printing source code {:?}", s);
-                *out = format!("{:#?}", krate);
-            });
-        }
+        HirTree => call_with_pp_support_hir(&PpHirMode::Normal, tcx, move |_annotation, krate| {
+            debug!("pretty printing HIR tree");
+            format!("{:#?}", krate)
+        }),
 
         _ => unreachable!(),
-    }
+    };
 
     write_or_print(&out, ofile);
 }
@@ -493,14 +479,10 @@ fn print_with_analysis(
     tcx.analysis(LOCAL_CRATE)?;
 
     match ppm {
-        PpmMir | PpmMirCFG => match ppm {
-            PpmMir => write_mir_pretty(tcx, None, &mut out),
-            PpmMirCFG => write_mir_graphviz(tcx, None, &mut out),
-            _ => unreachable!(),
-        },
+        Mir => write_mir_pretty(tcx, None, &mut out).unwrap(),
+        MirCFG => write_mir_graphviz(tcx, None, &mut out).unwrap(),
         _ => unreachable!(),
     }
-    .unwrap();
 
     let out = std::str::from_utf8(&out).unwrap();
     write_or_print(out, ofile);
index e112674fbcc49b7d95d33a903ffc6b2bd2ee022d..760c5897698aa6137cf5767a1d19ea16f2f6c81c 100644 (file)
@@ -2,7 +2,7 @@ Multiple candidate files were found for an out-of-line module.
 
 Erroneous code example:
 
-```ignore (multiple source files required for compile_fail)
+```ignore (Multiple source files are required for compile_fail.)
 // file: ambiguous_module/mod.rs
 
 fn foo() {}
index ef4a45cab41356128d6916bbfb95131e51c4d3e1..ce5b130dd97fee2e8ce820bc97f069b56d9bd2fb 100644 (file)
@@ -295,6 +295,7 @@ pub fn multipart_suggestion(
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
     ) -> &mut Self {
+        assert!(!suggestion.is_empty());
         self.suggestions.push(CodeSuggestion {
             substitutions: vec![Substitution {
                 parts: suggestion
@@ -318,6 +319,10 @@ pub fn multipart_suggestions(
         suggestions: Vec<Vec<(Span, String)>>,
         applicability: Applicability,
     ) -> &mut Self {
+        assert!(!suggestions.is_empty());
+        for s in &suggestions {
+            assert!(!s.is_empty());
+        }
         self.suggestions.push(CodeSuggestion {
             substitutions: suggestions
                 .into_iter()
@@ -348,6 +353,7 @@ pub fn tool_only_multipart_suggestion(
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
     ) -> &mut Self {
+        assert!(!suggestion.is_empty());
         self.suggestions.push(CodeSuggestion {
             substitutions: vec![Substitution {
                 parts: suggestion
index ac50703b5444e91f494dd89df7009357c3007357..072062dd615d874942f0102884b342199bd17fd5 100644 (file)
@@ -470,6 +470,7 @@ macro_rules! experimental {
 
     rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL),
     rustc_attr!(rustc_args_required_const, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE),
+    rustc_attr!(rustc_legacy_const_generics, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE),
 
     // ==========================================================================
     // Internal attributes, Layout related:
index 2fef7c2cc087d4097e1d388ace6a2db18dec53f0..f4402843afcbe5bd678bfc1e4ffb8c18b22c3024 100644 (file)
@@ -1577,6 +1577,14 @@ pub fn peel_drop_temps(&self) -> &Self {
         expr
     }
 
+    pub fn peel_blocks(&self) -> &Self {
+        let mut expr = self;
+        while let ExprKind::Block(Block { expr: Some(inner), .. }, _) = &expr.kind {
+            expr = inner;
+        }
+        expr
+    }
+
     pub fn can_have_side_effects(&self) -> bool {
         match self.peel_drop_temps().kind {
             ExprKind::Path(_) | ExprKind::Lit(_) => false,
index 6358855ac322e2ae68d0f615b8376937e3c7016f..5217066bbefdec8f5b3a3f786fb8083287786563 100644 (file)
@@ -350,7 +350,7 @@ fn configure_and_expand_inner<'a>(
         rustc_builtin_macros::test_harness::inject(&sess, &mut resolver, &mut krate)
     });
 
-    if let Some(PpMode::PpmSource(PpSourceMode::PpmEveryBodyLoops)) = sess.opts.pretty {
+    if let Some(PpMode::Source(PpSourceMode::EveryBodyLoops)) = sess.opts.pretty {
         tracing::debug!("replacing bodies with loop {{}}");
         util::ReplaceBodyWithLoop::new(&mut resolver).visit_crate(&mut krate);
     }
index 828c025d38d0bfe62a1874f28e09fd38540adc98..0f860d11dc21a551b7baa68ca42e6f1e47ab9c84 100644 (file)
@@ -468,6 +468,10 @@ pub fn module_expansion_untracked(&self, def_id: DefId, sess: &Session) -> ExpnI
     pub fn num_def_ids(&self, cnum: CrateNum) -> usize {
         self.get_crate_data(cnum).num_def_ids()
     }
+
+    pub fn item_attrs(&self, def_id: DefId, sess: &Session) -> Vec<ast::Attribute> {
+        self.get_crate_data(def_id.krate).get_item_attrs(def_id.index, sess).collect()
+    }
 }
 
 impl CrateStore for CStore {
index ca73481b21699bed3d020a79ec8135f674a543d2..26e61ec8cf8666483ba5ebb14715069a00bee1f0 100644 (file)
@@ -353,12 +353,12 @@ fn struct_lint_level_impl(
                  it will become a hard error";
 
             let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) {
-                "once this method is added to the standard library, \
-                 the ambiguity may cause an error or change in behavior!"
+                "once this associated item is added to the standard library, the ambiguity may \
+                 cause an error or change in behavior!"
                     .to_owned()
             } else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) {
-                "this borrowing pattern was not meant to be accepted, \
-                 and may become a hard error in the future"
+                "this borrowing pattern was not meant to be accepted, and may become a hard error \
+                 in the future"
                     .to_owned()
             } else if let Some(edition) = future_incompatible.edition {
                 format!("{} in the {} edition!", STANDARD_MESSAGE, edition)
index 449b3a82dcbb7c68f8423071d3a85975fa21b461..48595f2badc8fcb99fcd8b545d6806efde74cb9f 100644 (file)
@@ -543,12 +543,12 @@ fn is_init(&self, ptr: Pointer<Tag>, size: Size) -> Result<(), Range<Size>> {
     /// error which will report the first range of bytes which is uninitialized.
     fn check_init(&self, ptr: Pointer<Tag>, size: Size) -> InterpResult<'tcx> {
         self.is_init(ptr, size).or_else(|idx_range| {
-            throw_ub!(InvalidUninitBytes(Some(Box::new(UninitBytesAccess {
+            throw_ub!(InvalidUninitBytes(Some(UninitBytesAccess {
                 access_ptr: ptr.erase_tag(),
                 access_size: size,
                 uninit_ptr: Pointer::new(ptr.alloc_id, idx_range.start),
                 uninit_size: idx_range.end - idx_range.start, // `Size` subtraction
-            }))))
+            })))
         })
     }
 
index 26ce3c2c3db8a8e38fd03cab6c5bc1c138b8d2d7..1589ab28e4043d1713bd6fbfe116b64504b40f57 100644 (file)
@@ -9,7 +9,7 @@
 use rustc_session::CtfeBacktrace;
 use rustc_span::def_id::DefId;
 use rustc_target::abi::{Align, Size};
-use std::{any::Any, backtrace::Backtrace, fmt, mem};
+use std::{any::Any, backtrace::Backtrace, fmt};
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
 pub enum ErrorHandled {
@@ -263,7 +263,7 @@ pub enum UndefinedBehaviorInfo<'tcx> {
     /// Using a string that is not valid UTF-8,
     InvalidStr(std::str::Utf8Error),
     /// Using uninitialized data where it is not allowed.
-    InvalidUninitBytes(Option<Box<UninitBytesAccess>>),
+    InvalidUninitBytes(Option<UninitBytesAccess>),
     /// Working with a local that is not currently live.
     DeadLocal,
     /// Data size is not equal to target size.
@@ -445,7 +445,7 @@ pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
 }
 
 #[cfg(target_arch = "x86_64")]
-static_assert_size!(InterpError<'_>, 40);
+static_assert_size!(InterpError<'_>, 72);
 
 pub enum InterpError<'tcx> {
     /// The program caused undefined behavior.
@@ -486,19 +486,14 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 }
 
 impl InterpError<'_> {
-    /// Some errors allocate to be created as they contain free-form strings.
-    /// And sometimes we want to be sure that did not happen as it is a
-    /// waste of resources.
-    pub fn allocates(&self) -> bool {
+    /// Some errors to string formatting even if the error is never printed.
+    /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors,
+    /// so this method lets us detect them and `bug!` on unexpected errors.
+    pub fn formatted_string(&self) -> bool {
         match self {
-            // Zero-sized boxes do not allocate.
-            InterpError::MachineStop(b) => mem::size_of_val::<dyn MachineStopType>(&**b) > 0,
             InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
             | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_))
-            | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
-            | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some(_))) => {
-                true
-            }
+            | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) => true,
             _ => false,
         }
     }
index 4207e2dea34a3fc5278b240c8263f7e83d95f161..a8600af1de28e9806f190a6c9311408ec493a011 100644 (file)
         desc { |tcx| "computing normalized predicates of `{}`", tcx.def_path_str(def_id) }
     }
 
-    /// Like `param_env`, but returns the `ParamEnv in `Reveal::All` mode.
+    /// Like `param_env`, but returns the `ParamEnv` in `Reveal::All` mode.
     /// Prefer this over `tcx.param_env(def_id).with_reveal_all_normalized(tcx)`,
     /// as this method is more efficient.
     query param_env_reveal_all_normalized(def_id: DefId) -> ty::ParamEnv<'tcx> {
     query normalize_opaque_types(key: &'tcx ty::List<ty::Predicate<'tcx>>) -> &'tcx ty::List<ty::Predicate<'tcx>> {
         desc { "normalizing opaque types in {:?}", key }
     }
+
+    /// Checks whether a type is definitely uninhabited. This is
+    /// conservative: for some types that are uninhabited we return `false`,
+    /// but we only return `true` for types that are definitely uninhabited.
+    /// `ty.conservative_is_privately_uninhabited` implies that any value of type `ty`
+    /// will be `Abi::Uninhabited`. (Note that uninhabited types may have nonzero
+    /// size, to account for partial initialisation. See #49298 for details.)
+    query conservative_is_privately_uninhabited(key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+        desc { "conservatively checking if {:?} is privately uninhabited", key }
+    }
 }
index 12dcb95187cff4b32ae52ada59f6448a0ca0fddd..725c144257c4cb1a005a10707ebe215caa7e12e8 100644 (file)
@@ -232,7 +232,7 @@ fn layout_raw<'tcx>(
             let layout = cx.layout_raw_uncached(ty);
             // Type-level uninhabitedness should always imply ABI uninhabitedness.
             if let Ok(layout) = layout {
-                if ty.conservative_is_privately_uninhabited(tcx) {
+                if tcx.conservative_is_privately_uninhabited(param_env.and(ty)) {
                     assert!(layout.abi.is_uninhabited());
                 }
             }
@@ -584,11 +584,12 @@ fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx Layout, LayoutError<
                 let size =
                     element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow(ty))?;
 
-                let abi = if count != 0 && ty.conservative_is_privately_uninhabited(tcx) {
-                    Abi::Uninhabited
-                } else {
-                    Abi::Aggregate { sized: true }
-                };
+                let abi =
+                    if count != 0 && tcx.conservative_is_privately_uninhabited(param_env.and(ty)) {
+                        Abi::Uninhabited
+                    } else {
+                        Abi::Aggregate { sized: true }
+                    };
 
                 let largest_niche = if count != 0 { element.largest_niche.clone() } else { None };
 
index bbf64c69d83bd399f2ccb57e6cb3dbe465869693..a7bcc198f02c99767fd9d70743c809635620273f 100644 (file)
@@ -1697,53 +1697,6 @@ pub fn is_never(&self) -> bool {
         matches!(self.kind(), Never)
     }
 
-    /// Checks whether a type is definitely uninhabited. This is
-    /// conservative: for some types that are uninhabited we return `false`,
-    /// but we only return `true` for types that are definitely uninhabited.
-    /// `ty.conservative_is_privately_uninhabited` implies that any value of type `ty`
-    /// will be `Abi::Uninhabited`. (Note that uninhabited types may have nonzero
-    /// size, to account for partial initialisation. See #49298 for details.)
-    pub fn conservative_is_privately_uninhabited(&self, tcx: TyCtxt<'tcx>) -> bool {
-        // FIXME(varkor): we can make this less conversative by substituting concrete
-        // type arguments.
-        match self.kind() {
-            ty::Never => true,
-            ty::Adt(def, _) if def.is_union() => {
-                // For now, `union`s are never considered uninhabited.
-                false
-            }
-            ty::Adt(def, _) => {
-                // Any ADT is uninhabited if either:
-                // (a) It has no variants (i.e. an empty `enum`);
-                // (b) Each of its variants (a single one in the case of a `struct`) has at least
-                //     one uninhabited field.
-                def.variants.iter().all(|var| {
-                    var.fields.iter().any(|field| {
-                        tcx.type_of(field.did).conservative_is_privately_uninhabited(tcx)
-                    })
-                })
-            }
-            ty::Tuple(..) => {
-                self.tuple_fields().any(|ty| ty.conservative_is_privately_uninhabited(tcx))
-            }
-            ty::Array(ty, len) => {
-                match len.try_eval_usize(tcx, ParamEnv::empty()) {
-                    Some(0) | None => false,
-                    // If the array is definitely non-empty, it's uninhabited if
-                    // the type of its elements is uninhabited.
-                    Some(1..) => ty.conservative_is_privately_uninhabited(tcx),
-                }
-            }
-            ty::Ref(..) => {
-                // References to uninitialised memory is valid for any type, including
-                // uninhabited types, in unsafe code, so we treat all references as
-                // inhabited.
-                false
-            }
-            _ => false,
-        }
-    }
-
     #[inline]
     pub fn is_primitive(&self) -> bool {
         self.kind().is_primitive()
index f64848e694ccac0d8f8cb35bf3138fdff29c4d6e..aa946fdafa912f2e15996e993cdc4f2b36cd6bd3 100644 (file)
@@ -1734,7 +1734,10 @@ fn check_call_dest(
                 }
             }
             None => {
-                if !sig.output().conservative_is_privately_uninhabited(self.tcx()) {
+                if !self
+                    .tcx()
+                    .conservative_is_privately_uninhabited(self.param_env.and(sig.output()))
+                {
                     span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
                 }
             }
index 524ad0af1a7b45d4c85de9b6404deeca35bc6730..3f7808c20901b13418db34826d4b698269fecadb 100644 (file)
@@ -10,7 +10,7 @@
 //! fixpoint solution to your dataflow problem, or implement the `ResultsVisitor` interface and use
 //! `visit_results`. The following example uses the `ResultsCursor` approach.
 //!
-//! ```ignore(cross-crate-imports)
+//! ```ignore (cross-crate-imports)
 //! use rustc_mir::dataflow::Analysis; // Makes `into_engine` available.
 //!
 //! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>) {
@@ -211,7 +211,7 @@ fn apply_switch_int_edge_effects(
     /// default impl and the one for all `A: GenKillAnalysis` will do the right thing.
     /// Its purpose is to enable method chaining like so:
     ///
-    /// ```ignore(cross-crate-imports)
+    /// ```ignore (cross-crate-imports)
     /// let results = MyAnalysis::new(tcx, body)
     ///     .into_engine(tcx, body, def_id)
     ///     .iterate_to_fixpoint()
index 59438661cac7d3dbe1f180bcf7b67f5fcc66b42c..95464da145cfd51b2ae17e06325413c8b70f89c4 100644 (file)
@@ -352,14 +352,6 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx, const_eval
                         error
                     ),
                 );
-                // Some errors shouldn't come up because creating them causes
-                // an allocation, which we should avoid. When that happens,
-                // dedicated error variants should be introduced instead.
-                assert!(
-                    !error.kind().allocates(),
-                    "interning encountered allocating error: {}",
-                    error
-                );
             }
         }
     }
index 5384dbcb8a6a17eff9d37b355c6307b3621a60bc..3b27b544310a98dd1ba1f5a797a5182800e4fad3 100644 (file)
@@ -466,8 +466,8 @@ fn use_ecx<F, T>(&mut self, f: F) -> Option<T>
                 // an allocation, which we should avoid. When that happens,
                 // dedicated error variants should be introduced instead.
                 assert!(
-                    !error.kind().allocates(),
-                    "const-prop encountered allocating error: {}",
+                    !error.kind().formatted_string(),
+                    "const-prop encountered formatting error: {}",
                     error
                 );
                 None
index dc413f8dd2dc5b152ba44ebb7499698943df2daa..9f996e8452835260e16f48225256801b592b109d 100644 (file)
@@ -1007,9 +1007,9 @@ fn insert_panic_block<'tcx>(
     assert_block
 }
 
-fn can_return<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool {
+fn can_return<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
     // Returning from a function with an uninhabited return type is undefined behavior.
-    if body.return_ty().conservative_is_privately_uninhabited(tcx) {
+    if tcx.conservative_is_privately_uninhabited(param_env.and(body.return_ty())) {
         return false;
     }
 
@@ -1320,7 +1320,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         // `storage_liveness` tells us which locals have live storage at suspension points
         let (remap, layout, storage_liveness) = compute_layout(liveness_info, body);
 
-        let can_return = can_return(tcx, body);
+        let can_return = can_return(tcx, body, tcx.param_env(body.source.def_id()));
 
         // Run the transformation which converts Places from Local to generator struct
         // accesses for locals in `remap`.
index 073a2d8bd512006d06968457d1bd01e2d43457c5..9668a24bf8ab609f6802390fa59f4d0071b9340d 100644 (file)
@@ -225,7 +225,7 @@ fn parse_item_kind(
                 return Err(e);
             }
 
-            (Ident::invalid(), ItemKind::Use(P(tree)))
+            (Ident::invalid(), ItemKind::Use(tree))
         } else if self.check_fn_front_matter() {
             // FUNCTION ITEM
             let (ident, sig, generics, body) = self.parse_fn(attrs, req_name, lo)?;
index bf9b7e588bd4b471feec430408cb1fefb31da572..cf08881cd473da278458d0dcb849b8d1007ec1ec 100644 (file)
@@ -91,6 +91,8 @@ fn check_attributes(
                 self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
             } else if self.tcx.sess.check_name(attr, sym::naked) {
                 self.check_naked(hir_id, attr, span, target)
+            } else if self.tcx.sess.check_name(attr, sym::rustc_legacy_const_generics) {
+                self.check_rustc_legacy_const_generics(&attr, span, target, item)
             } else {
                 // lint-only checks
                 if self.tcx.sess.check_name(attr, sym::cold) {
@@ -750,6 +752,105 @@ fn check_rustc_args_required_const(
         }
     }
 
+    /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.
+    fn check_rustc_legacy_const_generics(
+        &self,
+        attr: &Attribute,
+        span: &Span,
+        target: Target,
+        item: Option<ItemLike<'_>>,
+    ) -> bool {
+        let is_function = matches!(target, Target::Fn | Target::Method(..));
+        if !is_function {
+            self.tcx
+                .sess
+                .struct_span_err(attr.span, "attribute should be applied to a function")
+                .span_label(*span, "not a function")
+                .emit();
+            return false;
+        }
+
+        let list = match attr.meta_item_list() {
+            // The attribute form is validated on AST.
+            None => return false,
+            Some(it) => it,
+        };
+
+        let (decl, generics) = match item {
+            Some(ItemLike::Item(Item {
+                kind: ItemKind::Fn(FnSig { decl, .. }, generics, _),
+                ..
+            })) => (decl, generics),
+            _ => bug!("should be a function item"),
+        };
+
+        for param in generics.params {
+            match param.kind {
+                hir::GenericParamKind::Const { .. } => {}
+                _ => {
+                    self.tcx
+                        .sess
+                        .struct_span_err(
+                            attr.span,
+                            "#[rustc_legacy_const_generics] functions must \
+                             only have const generics",
+                        )
+                        .span_label(param.span, "non-const generic parameter")
+                        .emit();
+                    return false;
+                }
+            }
+        }
+
+        if list.len() != generics.params.len() {
+            self.tcx
+                .sess
+                .struct_span_err(
+                    attr.span,
+                    "#[rustc_legacy_const_generics] must have one index for each generic parameter",
+                )
+                .span_label(generics.span, "generic parameters")
+                .emit();
+            return false;
+        }
+
+        let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
+        let mut invalid_args = vec![];
+        for meta in list {
+            if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
+                if *val >= arg_count {
+                    let span = meta.span();
+                    self.tcx
+                        .sess
+                        .struct_span_err(span, "index exceeds number of arguments")
+                        .span_label(
+                            span,
+                            format!(
+                                "there {} only {} argument{}",
+                                if arg_count != 1 { "are" } else { "is" },
+                                arg_count,
+                                pluralize!(arg_count)
+                            ),
+                        )
+                        .emit();
+                    return false;
+                }
+            } else {
+                invalid_args.push(meta.span());
+            }
+        }
+
+        if !invalid_args.is_empty() {
+            self.tcx
+                .sess
+                .struct_span_err(invalid_args, "arguments should be non-negative integers")
+                .emit();
+            false
+        } else {
+            true
+        }
+    }
+
     /// Checks if `#[link_section]` is applied to a function or static.
     fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
         match target {
index 701d48a982d36db8c9c1d040d2bfbe31b9e70801..6f24d7e9413518b6daf48b74a3b5d90fd5f28752 100644 (file)
@@ -2326,8 +2326,22 @@ fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) {
 
             ExprKind::Call(ref callee, ref arguments) => {
                 self.resolve_expr(callee, Some(expr));
-                for argument in arguments {
-                    self.resolve_expr(argument, None);
+                let const_args = self.r.legacy_const_generic_args(callee).unwrap_or(Vec::new());
+                for (idx, argument) in arguments.iter().enumerate() {
+                    // Constant arguments need to be treated as AnonConst since
+                    // that is how they will be later lowered to HIR.
+                    if const_args.contains(&idx) {
+                        self.with_constant_rib(
+                            IsRepeatExpr::No,
+                            argument.is_potential_trivial_const_param(),
+                            None,
+                            |this| {
+                                this.resolve_expr(argument, None);
+                            },
+                        );
+                    } else {
+                        self.resolve_expr(argument, None);
+                    }
                 }
             }
             ExprKind::Type(ref type_expr, ref ty) => {
index 6457c6cee576f556b247ec19206684af418d7fd3..d60f929cb8f3f131ff5f08da6ebb242a06dc9ab6 100644 (file)
@@ -319,9 +319,13 @@ pub(crate) fn smart_resolve_report_errors(
             .collect::<Vec<_>>();
         let crate_def_id = DefId::local(CRATE_DEF_INDEX);
         if candidates.is_empty() && is_expected(Res::Def(DefKind::Enum, crate_def_id)) {
-            let enum_candidates =
-                self.r.lookup_import_candidates(ident, ns, &self.parent_scope, is_enum_variant);
-
+            let mut enum_candidates: Vec<_> = self
+                .r
+                .lookup_import_candidates(ident, ns, &self.parent_scope, is_enum_variant)
+                .into_iter()
+                .map(|suggestion| import_candidate_to_enum_paths(&suggestion))
+                .filter(|(_, enum_ty_path)| enum_ty_path != "std::prelude::v1")
+                .collect();
             if !enum_candidates.is_empty() {
                 if let (PathSource::Type, Some(span)) =
                     (source, self.diagnostic_metadata.current_type_ascription.last())
@@ -340,10 +344,6 @@ pub(crate) fn smart_resolve_report_errors(
                     }
                 }
 
-                let mut enum_candidates = enum_candidates
-                    .iter()
-                    .map(|suggestion| import_candidate_to_enum_paths(&suggestion))
-                    .collect::<Vec<_>>();
                 enum_candidates.sort();
 
                 // Contextualize for E0412 "cannot find type", but don't belabor the point
@@ -363,19 +363,7 @@ pub(crate) fn smart_resolve_report_errors(
                 err.span_suggestions(
                     span,
                     &msg,
-                    enum_candidates
-                        .into_iter()
-                        .map(|(_variant_path, enum_ty_path)| enum_ty_path)
-                        // Variants re-exported in prelude doesn't mean `prelude::v1` is the
-                        // type name!
-                        // FIXME: is there a more principled way to do this that
-                        // would work for other re-exports?
-                        .filter(|enum_ty_path| enum_ty_path != "std::prelude::v1")
-                        // Also write `Option` rather than `std::prelude::v1::Option`.
-                        .map(|enum_ty_path| {
-                            // FIXME #56861: DRY-er prelude filtering.
-                            enum_ty_path.trim_start_matches("std::prelude::v1::").to_owned()
-                        }),
+                    enum_candidates.into_iter().map(|(_variant_path, enum_ty_path)| enum_ty_path),
                     Applicability::MachineApplicable,
                 );
             }
index 77fbbaa1532af38a15fa6b4967ff79688fa34ab6..e63fd4ce6357db25926f89ac376eafde1eae13fa 100644 (file)
@@ -29,6 +29,7 @@
 use rustc_ast::visit::{self, Visitor};
 use rustc_ast::{self as ast, NodeId};
 use rustc_ast::{Crate, CRATE_NODE_ID};
+use rustc_ast::{Expr, ExprKind, LitKind};
 use rustc_ast::{ItemKind, ModKind, Path};
 use rustc_ast_lowering::ResolverAstLowering;
 use rustc_ast_pretty::pprust;
@@ -995,6 +996,8 @@ pub struct Resolver<'a> {
     /// Some way to know that we are in a *trait* impl in `visit_assoc_item`.
     /// FIXME: Replace with a more general AST map (together with some other fields).
     trait_impl_items: FxHashSet<LocalDefId>,
+
+    legacy_const_generic_args: FxHashMap<DefId, Option<Vec<usize>>>,
 }
 
 /// Nothing really interesting here; it just provides memory for the rest of the crate.
@@ -1076,6 +1079,10 @@ fn item_generics_num_lifetimes(&self, def_id: DefId, sess: &Session) -> usize {
         self.cstore().item_generics_num_lifetimes(def_id, sess)
     }
 
+    fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option<Vec<usize>> {
+        self.legacy_const_generic_args(expr)
+    }
+
     fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes> {
         self.partial_res_map.get(&id).cloned()
     }
@@ -1316,6 +1323,7 @@ pub fn new(
             invocation_parents,
             next_disambiguator: Default::default(),
             trait_impl_items: Default::default(),
+            legacy_const_generic_args: Default::default(),
         };
 
         let root_parent_scope = ParentScope::module(graph_root, &resolver);
@@ -3308,6 +3316,61 @@ pub fn all_macros(&self) -> &FxHashMap<Symbol, Res> {
     pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
         if let Some(def_id) = def_id.as_local() { Some(self.def_id_to_span[def_id]) } else { None }
     }
+
+    /// Checks if an expression refers to a function marked with
+    /// `#[rustc_legacy_const_generics]` and returns the argument index list
+    /// from the attribute.
+    pub fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option<Vec<usize>> {
+        if let ExprKind::Path(None, path) = &expr.kind {
+            // Don't perform legacy const generics rewriting if the path already
+            // has generic arguments.
+            if path.segments.last().unwrap().args.is_some() {
+                return None;
+            }
+
+            let partial_res = self.partial_res_map.get(&expr.id)?;
+            if partial_res.unresolved_segments() != 0 {
+                return None;
+            }
+
+            if let Res::Def(def::DefKind::Fn, def_id) = partial_res.base_res() {
+                // We only support cross-crate argument rewriting. Uses
+                // within the same crate should be updated to use the new
+                // const generics style.
+                if def_id.is_local() {
+                    return None;
+                }
+
+                if let Some(v) = self.legacy_const_generic_args.get(&def_id) {
+                    return v.clone();
+                }
+
+                let parse_attrs = || {
+                    let attrs = self.cstore().item_attrs(def_id, self.session);
+                    let attr = attrs
+                        .iter()
+                        .find(|a| self.session.check_name(a, sym::rustc_legacy_const_generics))?;
+                    let mut ret = vec![];
+                    for meta in attr.meta_item_list()? {
+                        match meta.literal()?.kind {
+                            LitKind::Int(a, _) => {
+                                ret.push(a as usize);
+                            }
+                            _ => panic!("invalid arg index"),
+                        }
+                    }
+                    Some(ret)
+                };
+
+                // Cache the lookup to avoid parsing attributes for an iterm
+                // multiple times.
+                let ret = parse_attrs();
+                self.legacy_const_generic_args.insert(def_id, ret.clone());
+                return ret;
+            }
+        }
+        None
+    }
 }
 
 fn names_to_string(names: &[Symbol]) -> String {
index 81d6c3bbdbba25eee9441435d2c8eaee5774e5fc..38da52b88f36969ff48eda4dbbe63bca93eed987 100644 (file)
@@ -2057,40 +2057,21 @@ fn parse_pretty(
     debugging_opts: &DebuggingOptions,
     efmt: ErrorOutputType,
 ) -> Option<PpMode> {
-    let pretty = if debugging_opts.unstable_options {
-        matches.opt_default("pretty", "normal").map(|a| {
-            // stable pretty-print variants only
-            parse_pretty_inner(efmt, &a, false)
-        })
-    } else {
-        None
-    };
-
-    return if pretty.is_none() {
-        debugging_opts.unpretty.as_ref().map(|a| {
-            // extended with unstable pretty-print variants
-            parse_pretty_inner(efmt, &a, true)
-        })
-    } else {
-        pretty
-    };
-
     fn parse_pretty_inner(efmt: ErrorOutputType, name: &str, extended: bool) -> PpMode {
         use PpMode::*;
-        use PpSourceMode::*;
         let first = match (name, extended) {
-            ("normal", _) => PpmSource(PpmNormal),
-            ("identified", _) => PpmSource(PpmIdentified),
-            ("everybody_loops", true) => PpmSource(PpmEveryBodyLoops),
-            ("expanded", _) => PpmSource(PpmExpanded),
-            ("expanded,identified", _) => PpmSource(PpmExpandedIdentified),
-            ("expanded,hygiene", _) => PpmSource(PpmExpandedHygiene),
-            ("hir", true) => PpmHir(PpmNormal),
-            ("hir,identified", true) => PpmHir(PpmIdentified),
-            ("hir,typed", true) => PpmHir(PpmTyped),
-            ("hir-tree", true) => PpmHirTree(PpmNormal),
-            ("mir", true) => PpmMir,
-            ("mir-cfg", true) => PpmMirCFG,
+            ("normal", _) => Source(PpSourceMode::Normal),
+            ("identified", _) => Source(PpSourceMode::Identified),
+            ("everybody_loops", true) => Source(PpSourceMode::EveryBodyLoops),
+            ("expanded", _) => Source(PpSourceMode::Expanded),
+            ("expanded,identified", _) => Source(PpSourceMode::ExpandedIdentified),
+            ("expanded,hygiene", _) => Source(PpSourceMode::ExpandedHygiene),
+            ("hir", true) => Hir(PpHirMode::Normal),
+            ("hir,identified", true) => Hir(PpHirMode::Identified),
+            ("hir,typed", true) => Hir(PpHirMode::Typed),
+            ("hir-tree", true) => HirTree,
+            ("mir", true) => Mir,
+            ("mir-cfg", true) => MirCFG,
             _ => {
                 if extended {
                     early_error(
@@ -2119,6 +2100,18 @@ fn parse_pretty_inner(efmt: ErrorOutputType, name: &str, extended: bool) -> PpMo
         tracing::debug!("got unpretty option: {:?}", first);
         first
     }
+
+    if debugging_opts.unstable_options {
+        if let Some(a) = matches.opt_default("pretty", "normal") {
+            // stable pretty-print variants only
+            return Some(parse_pretty_inner(efmt, &a, false));
+        }
+    }
+
+    debugging_opts.unpretty.as_ref().map(|a| {
+        // extended with unstable pretty-print variants
+        parse_pretty_inner(efmt, &a, true)
+    })
 }
 
 pub fn make_crate_type_option() -> RustcOptGroup {
@@ -2226,22 +2219,43 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum PpSourceMode {
-    PpmNormal,
-    PpmEveryBodyLoops,
-    PpmExpanded,
-    PpmIdentified,
-    PpmExpandedIdentified,
-    PpmExpandedHygiene,
-    PpmTyped,
+    /// `--pretty=normal`
+    Normal,
+    /// `-Zunpretty=everybody_loops`
+    EveryBodyLoops,
+    /// `--pretty=expanded`
+    Expanded,
+    /// `--pretty=identified`
+    Identified,
+    /// `--pretty=expanded,identified`
+    ExpandedIdentified,
+    /// `--pretty=expanded,hygiene`
+    ExpandedHygiene,
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum PpHirMode {
+    /// `-Zunpretty=hir`
+    Normal,
+    /// `-Zunpretty=hir,identified`
+    Identified,
+    /// `-Zunpretty=hir,typed`
+    Typed,
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum PpMode {
-    PpmSource(PpSourceMode),
-    PpmHir(PpSourceMode),
-    PpmHirTree(PpSourceMode),
-    PpmMir,
-    PpmMirCFG,
+    /// Options that print the source code, i.e.
+    /// `--pretty` and `-Zunpretty=everybody_loops`
+    Source(PpSourceMode),
+    /// Options that print the HIR, i.e. `-Zunpretty=hir`
+    Hir(PpHirMode),
+    /// `-Zunpretty=hir-tree`
+    HirTree,
+    /// `-Zunpretty=mir`
+    Mir,
+    /// `-Zunpretty=mir-cfg`
+    MirCFG,
 }
 
 impl PpMode {
@@ -2249,22 +2263,19 @@ pub fn needs_ast_map(&self) -> bool {
         use PpMode::*;
         use PpSourceMode::*;
         match *self {
-            PpmSource(PpmNormal | PpmIdentified) => false,
+            Source(Normal | Identified) => false,
 
-            PpmSource(
-                PpmExpanded | PpmEveryBodyLoops | PpmExpandedIdentified | PpmExpandedHygiene,
-            )
-            | PpmHir(_)
-            | PpmHirTree(_)
-            | PpmMir
-            | PpmMirCFG => true,
-            PpmSource(PpmTyped) => panic!("invalid state"),
+            Source(Expanded | EveryBodyLoops | ExpandedIdentified | ExpandedHygiene)
+            | Hir(_)
+            | HirTree
+            | Mir
+            | MirCFG => true,
         }
     }
 
     pub fn needs_analysis(&self) -> bool {
         use PpMode::*;
-        matches!(*self, PpmMir | PpmMirCFG)
+        matches!(*self, Mir | MirCFG)
     }
 }
 
index b112402ffe37136728dbee3772356b1fd694765a..6fe65a8a1cda1f4764efe66dea528fc2e637067c 100644 (file)
         rustc_layout,
         rustc_layout_scalar_valid_range_end,
         rustc_layout_scalar_valid_range_start,
+        rustc_legacy_const_generics,
         rustc_macro_transparency,
         rustc_mir,
         rustc_nonnull_optimization_guaranteed,
index 904c0062a926fae436baeca592d1db14ecabd351..aa5d3388401ae7651283547e0da3d52108c6e0db 100644 (file)
@@ -5,6 +5,8 @@
 //! This API is completely unstable and subject to change.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![feature(half_open_range_patterns)]
+#![feature(exclusive_range_pattern)]
 #![feature(nll)]
 #![recursion_limit = "256"]
 
index 76d27c292262d8005500a67b5be2ca99cf80b1dc..29f1761b84d2b3e54b4bf5d4340535cf452ab0a4 100644 (file)
@@ -481,6 +481,63 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync {
     fn_like.asyncness()
 }
 
+/// Don't call this directly: use ``tcx.conservative_is_privately_uninhabited`` instead.
+#[instrument(level = "debug", skip(tcx))]
+pub fn conservative_is_privately_uninhabited_raw<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env_and: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+) -> bool {
+    let (param_env, ty) = param_env_and.into_parts();
+    match ty.kind() {
+        ty::Never => {
+            debug!("ty::Never =>");
+            true
+        }
+        ty::Adt(def, _) if def.is_union() => {
+            debug!("ty::Adt(def, _) if def.is_union() =>");
+            // For now, `union`s are never considered uninhabited.
+            false
+        }
+        ty::Adt(def, substs) => {
+            debug!("ty::Adt(def, _) if def.is_not_union() =>");
+            // Any ADT is uninhabited if either:
+            // (a) It has no variants (i.e. an empty `enum`);
+            // (b) Each of its variants (a single one in the case of a `struct`) has at least
+            //     one uninhabited field.
+            def.variants.iter().all(|var| {
+                var.fields.iter().any(|field| {
+                    let ty = tcx.type_of(field.did).subst(tcx, substs);
+                    tcx.conservative_is_privately_uninhabited(param_env.and(ty))
+                })
+            })
+        }
+        ty::Tuple(..) => {
+            debug!("ty::Tuple(..) =>");
+            ty.tuple_fields().any(|ty| tcx.conservative_is_privately_uninhabited(param_env.and(ty)))
+        }
+        ty::Array(ty, len) => {
+            debug!("ty::Array(ty, len) =>");
+            match len.try_eval_usize(tcx, param_env) {
+                Some(0) | None => false,
+                // If the array is definitely non-empty, it's uninhabited if
+                // the type of its elements is uninhabited.
+                Some(1..) => tcx.conservative_is_privately_uninhabited(param_env.and(ty)),
+            }
+        }
+        ty::Ref(..) => {
+            debug!("ty::Ref(..) =>");
+            // References to uninitialised memory is valid for any type, including
+            // uninhabited types, in unsafe code, so we treat all references as
+            // inhabited.
+            false
+        }
+        _ => {
+            debug!("_ =>");
+            false
+        }
+    }
+}
+
 pub fn provide(providers: &mut ty::query::Providers) {
     *providers = ty::query::Providers {
         asyncness,
@@ -498,6 +555,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
         instance_def_size_estimate,
         issue33140_self_ty,
         impl_defaultness,
+        conservative_is_privately_uninhabited: conservative_is_privately_uninhabited_raw,
         ..*providers
     };
 }
index f2fbb95fc021cf5477605163465a4f4a72d4e0d0..ca1e79fac73e9d8fea6d9c7245fc8777070328ad 100644 (file)
@@ -4,7 +4,7 @@
 
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
-use rustc_hir::def::Res;
+use rustc_hir::def::{Namespace, Res};
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::{infer, traits};
@@ -374,7 +374,26 @@ fn confirm_builtin_call(
                                     |p| format!("`{}` defined here returns `{}`", p, callee_ty),
                                 )
                             }
-                            _ => Some(format!("`{}` defined here", callee_ty)),
+                            _ => {
+                                match def {
+                                    // Emit a different diagnostic for local variables, as they are not
+                                    // type definitions themselves, but rather variables *of* that type.
+                                    Res::Local(hir_id) => Some(format!(
+                                        "`{}` has type `{}`",
+                                        self.tcx.hir().name(hir_id),
+                                        callee_ty
+                                    )),
+                                    Res::Def(kind, def_id)
+                                        if kind.ns() == Some(Namespace::ValueNS) =>
+                                    {
+                                        Some(format!(
+                                            "`{}` defined here",
+                                            self.tcx.def_path_str(def_id),
+                                        ))
+                                    }
+                                    _ => Some(format!("`{}` defined here", callee_ty)),
+                                }
+                            }
                         };
                         if let Some(label) = label {
                             err.span_label(span, label);
index e3aacc44a02ee6d58711f891072c5e368beb68ad..32b3d0b059545b53d0b9ffe03f3404f539a0ac32 100644 (file)
@@ -42,6 +42,7 @@
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::{Coercion, InferOk, InferResult};
+use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::adjustment::{
     Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast,
 };
@@ -1448,7 +1449,12 @@ fn report_return_mismatched_types<'a>(
                 expected.is_unit(),
                 pointing_at_return_type,
             ) {
-                if cond_expr.span.desugaring_kind().is_none() {
+                // If the block is from an external macro, then do not suggest
+                // adding a semicolon, because there's nowhere to put it.
+                // See issue #81943.
+                if cond_expr.span.desugaring_kind().is_none()
+                    && !in_external_macro(fcx.tcx.sess, cond_expr.span)
+                {
                     err.span_label(cond_expr.span, "expected this to be `()`");
                     if expr.can_have_side_effects() {
                         fcx.suggest_semicolon_at_end(cond_expr.span, &mut err);
index 8d2004a543b7b1b151f6a088a370f6b010aa32d5..39b973ed371aeab78c0766acf1218f659fd1e320 100644 (file)
@@ -616,10 +616,30 @@ pub fn check_ref(
             }
             _ if sp == expr.span && !is_macro => {
                 if let Some(steps) = self.deref_steps(checked_ty, expected) {
+                    let expr = expr.peel_blocks();
+
                     if steps == 1 {
-                        // For a suggestion to make sense, the type would need to be `Copy`.
-                        if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp) {
-                            if let Ok(code) = sm.span_to_snippet(sp) {
+                        if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind {
+                            // If the expression has `&`, removing it would fix the error
+                            let prefix_span = expr.span.with_hi(inner.span.lo());
+                            let message = match mutbl {
+                                hir::Mutability::Not => "consider removing the `&`",
+                                hir::Mutability::Mut => "consider removing the `&mut`",
+                            };
+                            let suggestion = String::new();
+                            return Some((
+                                prefix_span,
+                                message,
+                                suggestion,
+                                Applicability::MachineApplicable,
+                            ));
+                        } else if self.infcx.type_is_copy_modulo_regions(
+                            self.param_env,
+                            expected,
+                            sp,
+                        ) {
+                            // For this suggestion to make sense, the type would need to be `Copy`.
+                            if let Ok(code) = sm.span_to_snippet(expr.span) {
                                 let message = if checked_ty.is_region_ptr() {
                                     "consider dereferencing the borrow"
                                 } else {
@@ -631,7 +651,7 @@ pub fn check_ref(
                                     format!("*{}", code)
                                 };
                                 return Some((
-                                    sp,
+                                    expr.span,
                                     message,
                                     suggestion,
                                     Applicability::MachineApplicable,
index 416b75d9e2e0c0c51bbfae4c2c1753272c1dcf92..1f50ad5779e0f187193fe70497cdc79661efb5ea 100644 (file)
@@ -10,6 +10,7 @@
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{ExprKind, ItemKind, Node};
 use rustc_infer::infer;
+use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::symbol::kw;
 
@@ -44,7 +45,10 @@ pub fn suggest_mismatched_types_on_tail(
         blk_id: hir::HirId,
     ) -> bool {
         let expr = expr.peel_drop_temps();
-        if expr.can_have_side_effects() {
+        // If the expression is from an external macro, then do not suggest
+        // adding a semicolon, because there's nowhere to put it.
+        // See issue #81943.
+        if expr.can_have_side_effects() && !in_external_macro(self.tcx.sess, cause_span) {
             self.suggest_missing_semicolon(err, expr, expected, cause_span);
         }
         let mut pointing_at_return_type = false;
index 158c214759db27c507c325742742cb15e2a836e4..c25aec5b2b7e9bdd2c35813337b33185f738e577 100644 (file)
@@ -10,6 +10,7 @@
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lrc;
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::def::Namespace;
 use rustc_infer::infer::canonical::OriginalQueryValues;
@@ -159,21 +160,21 @@ pub struct Pick<'tcx> {
     pub kind: PickKind<'tcx>,
     pub import_ids: SmallVec<[LocalDefId; 1]>,
 
-    // Indicates that the source expression should be autoderef'd N times
-    //
-    // A = expr | *expr | **expr | ...
+    /// Indicates that the source expression should be autoderef'd N times
+    ///
+    ///     A = expr | *expr | **expr | ...
     pub autoderefs: usize,
 
-    // Indicates that an autoref is applied after the optional autoderefs
-    //
-    // B = A | &A | &mut A
+    /// Indicates that an autoref is applied after the optional autoderefs
+    ///
+    ///     B = A | &A | &mut A
     pub autoref: Option<hir::Mutability>,
 
-    // Indicates that the source expression should be "unsized" to a
-    // target type. This should probably eventually go away in favor
-    // of just coercing method receivers.
-    //
-    // C = B | unsize(B)
+    /// Indicates that the source expression should be "unsized" to a
+    /// target type. This should probably eventually go away in favor
+    /// of just coercing method receivers.
+    ///
+    ///     C = B | unsize(B)
     pub unsize: Option<Ty<'tcx>>,
 }
 
@@ -1090,19 +1091,17 @@ fn pick_core(&mut self) -> Option<PickResult<'tcx>> {
             .next()
     }
 
+    /// For each type `T` in the step list, this attempts to find a method where
+    /// the (transformed) self type is exactly `T`. We do however do one
+    /// transformation on the adjustment: if we are passing a region pointer in,
+    /// we will potentially *reborrow* it to a shorter lifetime. This allows us
+    /// to transparently pass `&mut` pointers, in particular, without consuming
+    /// them for their entire lifetime.
     fn pick_by_value_method(
         &mut self,
         step: &CandidateStep<'tcx>,
         self_ty: Ty<'tcx>,
     ) -> Option<PickResult<'tcx>> {
-        //! For each type `T` in the step list, this attempts to find a
-        //! method where the (transformed) self type is exactly `T`. We
-        //! do however do one transformation on the adjustment: if we
-        //! are passing a region pointer in, we will potentially
-        //! *reborrow* it to a shorter lifetime. This allows us to
-        //! transparently pass `&mut` pointers, in particular, without
-        //! consuming them for their entire lifetime.
-
         if step.unsize {
             return None;
         }
@@ -1167,7 +1166,7 @@ fn pick_method(&mut self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> {
                         //
                         // We suppress warning if we're picking the method only because it is a
                         // suggestion.
-                        self.emit_unstable_name_collision_hint(p, &unstable_candidates);
+                        self.emit_unstable_name_collision_hint(p, &unstable_candidates, self_ty);
                     }
                 }
                 return Some(pick);
@@ -1246,24 +1245,46 @@ fn emit_unstable_name_collision_hint(
         &self,
         stable_pick: &Pick<'_>,
         unstable_candidates: &[(&Candidate<'tcx>, Symbol)],
+        self_ty: Ty<'tcx>,
     ) {
         self.tcx.struct_span_lint_hir(
             lint::builtin::UNSTABLE_NAME_COLLISIONS,
             self.fcx.body_id,
             self.span,
             |lint| {
-                let mut diag = lint.build(
-                    "a method with this name may be added to the standard library in the future",
-                );
-                // FIXME: This should be a `span_suggestion` instead of `help`
-                // However `self.span` only
-                // highlights the method name, so we can't use it. Also consider reusing the code from
-                // `report_method_error()`.
-                diag.help(&format!(
-                    "call with fully qualified syntax `{}(...)` to keep using the current method",
-                    self.tcx.def_path_str(stable_pick.item.def_id),
+                let def_kind = stable_pick.item.kind.as_def_kind();
+                let mut diag = lint.build(&format!(
+                    "{} {} with this name may be added to the standard library in the future",
+                    def_kind.article(),
+                    def_kind.descr(stable_pick.item.def_id),
                 ));
-
+                match (stable_pick.item.kind, stable_pick.item.container) {
+                    (ty::AssocKind::Fn, _) => {
+                        // FIXME: This should be a `span_suggestion` instead of `help`
+                        // However `self.span` only
+                        // highlights the method name, so we can't use it. Also consider reusing
+                        // the code from `report_method_error()`.
+                        diag.help(&format!(
+                            "call with fully qualified syntax `{}(...)` to keep using the current \
+                             method",
+                            self.tcx.def_path_str(stable_pick.item.def_id),
+                        ));
+                    }
+                    (ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer(def_id)) => {
+                        diag.span_suggestion(
+                            self.span,
+                            "use the fully qualified path to the associated const",
+                            format!(
+                                "<{} as {}>::{}",
+                                self_ty,
+                                self.tcx.def_path_str(def_id),
+                                stable_pick.item.ident
+                            ),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                    _ => {}
+                }
                 if self.tcx.sess.is_nightly_build() {
                     for (candidate, feature) in unstable_candidates {
                         diag.help(&format!(
index 6cc8813bc523988fb554c7be51c0487e0e83ad19..941f82a8070a02e1861c23d57a567c4d2993ed4e 100644 (file)
@@ -278,14 +278,14 @@ pub fn into_key(self) -> K {
     pub fn insert(self, value: V) -> &'a mut V {
         let out_ptr = match self.handle.insert_recursing(self.key, value) {
             (Fit(_), val_ptr) => {
-                // Safety: We have consumed self.handle and the handle returned.
+                // SAFETY: We have consumed self.handle and the handle returned.
                 let map = unsafe { self.dormant_map.awaken() };
                 map.length += 1;
                 val_ptr
             }
             (Split(ins), val_ptr) => {
                 drop(ins.left);
-                // Safety: We have consumed self.handle and the reference returned.
+                // SAFETY: We have consumed self.handle and the reference returned.
                 let map = unsafe { self.dormant_map.awaken() };
                 let root = map.root.as_mut().unwrap();
                 root.push_internal_level().push(ins.kv.0, ins.kv.1, ins.right);
index dbb7708b60057795006abef3ffc59e450ecfd876..b1b2619428366fbfcb18d19a9432a0884d83aa0f 100644 (file)
@@ -1938,13 +1938,13 @@ pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit<T>] {
     pub fn split_at_spare_mut(&mut self) -> (&mut [T], &mut [MaybeUninit<T>]) {
         let ptr = self.as_mut_ptr();
 
-        // Safety:
+        // SAFETY:
         // - `ptr` is guaranteed to be in bounds for `capacity` elements
         // - `len` is guaranteed to less or equal to `capacity`
         // - `MaybeUninit<T>` has the same layout as `T`
         let spare_ptr = unsafe { ptr.cast::<MaybeUninit<T>>().add(self.len) };
 
-        // Safety:
+        // SAFETY:
         // - `ptr` is guaranteed to be valid for `len` elements
         // - `spare_ptr` is offseted from `ptr` by `len`, so it doesn't overlap `initialized` slice
         unsafe {
@@ -2154,7 +2154,8 @@ pub fn from_elem_in<T: Clone, A: Allocator>(elem: T, n: usize, alloc: A) -> Vec<
 }
 
 trait ExtendFromWithinSpec {
-    /// Safety:
+    /// # Safety
+    ///
     /// - `src` needs to be valid index
     /// - `self.capacity() - self.len()` must be `>= src.len()`
     unsafe fn spec_extend_from_within(&mut self, src: Range<usize>);
@@ -2165,14 +2166,14 @@ impl<T: Clone, A: Allocator> ExtendFromWithinSpec for Vec<T, A> {
         let initialized = {
             let (this, spare) = self.split_at_spare_mut();
 
-            // Safety:
+            // SAFETY:
             // - caller guaratees that src is a valid index
             let to_clone = unsafe { this.get_unchecked(src) };
 
             to_clone.iter().cloned().zip(spare.iter_mut()).map(|(e, s)| s.write(e)).count()
         };
 
-        // Safety:
+        // SAFETY:
         // - elements were just initialized
         unsafe {
             let new_len = self.len() + initialized;
@@ -2187,11 +2188,11 @@ unsafe fn spec_extend_from_within(&mut self, src: Range<usize>) {
         {
             let (init, spare) = self.split_at_spare_mut();
 
-            // Safety:
+            // SAFETY:
             // - caller guaratees that `src` is a valid index
             let source = unsafe { init.get_unchecked(src) };
 
-            // Safety:
+            // SAFETY:
             // - Both pointers are created from unique slice references (`&mut [_]`)
             //   so they are valid and do not overlap.
             // - Elements are :Copy so it's OK to to copy them, without doing
@@ -2203,7 +2204,7 @@ unsafe fn spec_extend_from_within(&mut self, src: Range<usize>) {
             unsafe { ptr::copy_nonoverlapping(source.as_ptr(), spare.as_mut_ptr() as _, count) };
         }
 
-        // Safety:
+        // SAFETY:
         // - The elements were just initialized by `copy_nonoverlapping`
         self.len += count;
     }
index 4390342134d1d6b0ab64f01f9e5dec7f4d248d97..b3e1becf570a7e697bd61dbb44c1c2fb76d38260 100644 (file)
@@ -569,8 +569,9 @@ pub fn escape_default(self) -> EscapeDefault {
     /// assert_eq!(len, tokyo.len());
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
+    #[rustc_const_stable(feature = "const_char_len_utf", since = "1.52.0")]
     #[inline]
-    pub fn len_utf8(self) -> usize {
+    pub const fn len_utf8(self) -> usize {
         len_utf8(self as u32)
     }
 
@@ -594,8 +595,9 @@ pub fn len_utf8(self) -> usize {
     /// assert_eq!(len, 2);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
+    #[rustc_const_stable(feature = "const_char_len_utf", since = "1.52.0")]
     #[inline]
-    pub fn len_utf16(self) -> usize {
+    pub const fn len_utf16(self) -> usize {
         let ch = self as u32;
         if (ch & 0xFFFF) == ch { 1 } else { 2 }
     }
@@ -1086,8 +1088,9 @@ pub const fn is_ascii(&self) -> bool {
     /// [`make_ascii_uppercase()`]: #method.make_ascii_uppercase
     /// [`to_uppercase()`]: #method.to_uppercase
     #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
+    #[rustc_const_stable(feature = "const_ascii_methods_on_intrinsics", since = "1.52.0")]
     #[inline]
-    pub fn to_ascii_uppercase(&self) -> char {
+    pub const fn to_ascii_uppercase(&self) -> char {
         if self.is_ascii_lowercase() {
             (*self as u8).ascii_change_case_unchecked() as char
         } else {
@@ -1118,8 +1121,9 @@ pub fn to_ascii_uppercase(&self) -> char {
     /// [`make_ascii_lowercase()`]: #method.make_ascii_lowercase
     /// [`to_lowercase()`]: #method.to_lowercase
     #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
+    #[rustc_const_stable(feature = "const_ascii_methods_on_intrinsics", since = "1.52.0")]
     #[inline]
-    pub fn to_ascii_lowercase(&self) -> char {
+    pub const fn to_ascii_lowercase(&self) -> char {
         if self.is_ascii_uppercase() {
             (*self as u8).ascii_change_case_unchecked() as char
         } else {
@@ -1143,8 +1147,9 @@ pub fn to_ascii_lowercase(&self) -> char {
     /// assert!(!upper_a.eq_ignore_ascii_case(&lower_z));
     /// ```
     #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
+    #[rustc_const_stable(feature = "const_ascii_methods_on_intrinsics", since = "1.52.0")]
     #[inline]
-    pub fn eq_ignore_ascii_case(&self, other: &char) -> bool {
+    pub const fn eq_ignore_ascii_case(&self, other: &char) -> bool {
         self.to_ascii_lowercase() == other.to_ascii_lowercase()
     }
 
@@ -1561,7 +1566,7 @@ pub const fn is_ascii_control(&self) -> bool {
 }
 
 #[inline]
-fn len_utf8(code: u32) -> usize {
+const fn len_utf8(code: u32) -> usize {
     if code < MAX_ONE_B {
         1
     } else if code < MAX_TWO_B {
index a9e2ef4251a16de56691ca462eb6c62d6264ca27..5274262ded2b20d9874b0586ba3ce8f4902a034c 100644 (file)
     ///
     /// This exists solely for [`mem::forget_unsized`]; normal `forget` uses
     /// `ManuallyDrop` instead.
+    #[rustc_const_unstable(feature = "const_intrinsic_forget", issue = "none")]
     pub fn forget<T: ?Sized>(_: T);
 
     /// Reinterprets the bits of a value of one type as another type.
index 081f282edcf895a3f9b64f930e4aef67633ed654..0114d7af4f41018ce1ab8ca806304001773db1df 100644 (file)
@@ -325,22 +325,28 @@ fn flatten<'a, T: IntoIterator, Acc, R: Try<Ok = Acc>>(
     }
 
     #[inline]
-    fn fold<Acc, Fold>(self, init: Acc, ref mut fold: Fold) -> Acc
+    fn fold<Acc, Fold>(self, mut init: Acc, mut fold: Fold) -> Acc
     where
         Fold: FnMut(Acc, Self::Item) -> Acc,
     {
         #[inline]
-        fn flatten<U: Iterator, Acc>(
-            fold: &mut impl FnMut(Acc, U::Item) -> Acc,
-        ) -> impl FnMut(Acc, U) -> Acc + '_ {
-            move |acc, iter| iter.fold(acc, &mut *fold)
+        fn flatten<T: IntoIterator, Acc>(
+            fold: &mut impl FnMut(Acc, T::Item) -> Acc,
+        ) -> impl FnMut(Acc, T) -> Acc + '_ {
+            move |acc, x| x.into_iter().fold(acc, &mut *fold)
         }
 
-        self.frontiter
-            .into_iter()
-            .chain(self.iter.map(IntoIterator::into_iter))
-            .chain(self.backiter)
-            .fold(init, flatten(fold))
+        if let Some(front) = self.frontiter {
+            init = front.fold(init, &mut fold);
+        }
+
+        init = self.iter.fold(init, flatten(&mut fold));
+
+        if let Some(back) = self.backiter {
+            init = back.fold(init, &mut fold);
+        }
+
+        init
     }
 }
 
@@ -411,21 +417,30 @@ fn flatten<'a, T: IntoIterator, Acc, R: Try<Ok = Acc>>(
     }
 
     #[inline]
-    fn rfold<Acc, Fold>(self, init: Acc, ref mut fold: Fold) -> Acc
+    fn rfold<Acc, Fold>(self, mut init: Acc, mut fold: Fold) -> Acc
     where
         Fold: FnMut(Acc, Self::Item) -> Acc,
     {
         #[inline]
-        fn flatten<U: DoubleEndedIterator, Acc>(
-            fold: &mut impl FnMut(Acc, U::Item) -> Acc,
-        ) -> impl FnMut(Acc, U) -> Acc + '_ {
-            move |acc, iter| iter.rfold(acc, &mut *fold)
+        fn flatten<T: IntoIterator, Acc>(
+            fold: &mut impl FnMut(Acc, T::Item) -> Acc,
+        ) -> impl FnMut(Acc, T) -> Acc + '_
+        where
+            T::IntoIter: DoubleEndedIterator,
+        {
+            move |acc, x| x.into_iter().rfold(acc, &mut *fold)
+        }
+
+        if let Some(back) = self.backiter {
+            init = back.rfold(init, &mut fold);
+        }
+
+        init = self.iter.rfold(init, flatten(&mut fold));
+
+        if let Some(front) = self.frontiter {
+            init = front.rfold(init, &mut fold);
         }
 
-        self.frontiter
-            .into_iter()
-            .chain(self.iter.map(IntoIterator::into_iter))
-            .chain(self.backiter)
-            .rfold(init, flatten(fold))
+        init
     }
 }
index 0d1a09a528db96642baefb04d800e26b731d9abd..64e2a95130999743982c84bbac9ab5a56b6ed23e 100644 (file)
 #![feature(const_discriminant)]
 #![feature(const_cell_into_inner)]
 #![feature(const_intrinsic_copy)]
+#![feature(const_intrinsic_forget)]
 #![feature(const_float_classify)]
 #![feature(const_float_bits_conv)]
 #![feature(const_int_unchecked_arith)]
 #![feature(const_mut_refs)]
+#![feature(const_refs_to_cell)]
 #![feature(const_cttz)]
 #![feature(const_panic)]
 #![feature(const_pin)]
@@ -90,6 +92,7 @@
 #![feature(const_ptr_offset)]
 #![feature(const_ptr_offset_from)]
 #![feature(const_ptr_read)]
+#![feature(const_ptr_write)]
 #![feature(const_raw_ptr_comparison)]
 #![feature(const_raw_ptr_deref)]
 #![feature(const_slice_from_raw_parts)]
index 41ea2760ec5705c0f73e38fbcf62e9af8c3832c2..9a54921f07b4985c4e1cae4a879d3d0a2ef78908 100644 (file)
@@ -1,4 +1,4 @@
-#[doc(include = "panic.md")]
+#[doc = include_str!("panic.md")]
 #[macro_export]
 #[rustc_builtin_macro = "core_panic"]
 #[allow_internal_unstable(edition_panic)]
index 9d8c8c862911c21bac4ce60cb482912ed41ea289..e2cc8faf8547d18157550b5621440060e38933fe 100644 (file)
@@ -54,10 +54,7 @@ fn from(never: !) -> TryFromIntError {
 ///
 /// Among other causes, `ParseIntError` can be thrown because of leading or trailing whitespace
 /// in the string e.g., when it is obtained from the standard input.
-/// Using the [`str.trim()`] method ensures that no whitespace remains before parsing.
-///
-/// [`str.trim()`]: ../../std/primitive.str.html#method.trim
-/// [`i8::from_str_radix`]: ../../std/primitive.i8.html#method.from_str_radix
+/// Using the [`str::trim()`] method ensures that no whitespace remains before parsing.
 ///
 /// # Example
 ///
index 795f94ec0345486ab433320bc5fcd7ab4cfa0025..80cfb5a0e3aeb2b519735cf0b439d7bb3be6f07e 100644 (file)
@@ -18,7 +18,7 @@
 use crate::num::FpCategory;
 
 /// The radix or base of the internal representation of `f32`.
-/// Use [`f32::RADIX`](../../std/primitive.f32.html#associatedconstant.RADIX) instead.
+/// Use [`f32::RADIX`] instead.
 ///
 /// # Examples
 ///
@@ -832,8 +832,8 @@ pub const fn from_bits(v: u32) -> Self {
     /// As the target platform's native endianness is used, portable code
     /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead.
     ///
-    /// [`to_be_bytes`]: #method.to_be_bytes
-    /// [`to_le_bytes`]: #method.to_le_bytes
+    /// [`to_be_bytes`]: f32::to_be_bytes
+    /// [`to_le_bytes`]: f32::to_le_bytes
     ///
     /// # Examples
     ///
@@ -860,7 +860,7 @@ pub const fn from_bits(v: u32) -> Self {
     ///
     /// [`to_ne_bytes`] should be preferred over this whenever possible.
     ///
-    /// [`to_ne_bytes`]: #method.to_ne_bytes
+    /// [`to_ne_bytes`]: f32::to_ne_bytes
     ///
     /// # Examples
     ///
@@ -920,8 +920,8 @@ pub const fn from_bits(v: u32) -> Self {
     /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as
     /// appropriate instead.
     ///
-    /// [`from_be_bytes`]: #method.from_be_bytes
-    /// [`from_le_bytes`]: #method.from_le_bytes
+    /// [`from_be_bytes`]: f32::from_be_bytes
+    /// [`from_le_bytes`]: f32::from_le_bytes
     ///
     /// # Examples
     ///
index 7af968f7fe84ba924b382192dbf661d6c17be18f..e365e1b21df10df0c648589f19e2c3b9c80fc1fd 100644 (file)
@@ -846,8 +846,8 @@ pub const fn from_bits(v: u64) -> Self {
     /// As the target platform's native endianness is used, portable code
     /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead.
     ///
-    /// [`to_be_bytes`]: #method.to_be_bytes
-    /// [`to_le_bytes`]: #method.to_le_bytes
+    /// [`to_be_bytes`]: f64::to_be_bytes
+    /// [`to_le_bytes`]: f64::to_le_bytes
     ///
     /// # Examples
     ///
@@ -874,7 +874,7 @@ pub const fn from_bits(v: u64) -> Self {
     ///
     /// [`to_ne_bytes`] should be preferred over this whenever possible.
     ///
-    /// [`to_ne_bytes`]: #method.to_ne_bytes
+    /// [`to_ne_bytes`]: f64::to_ne_bytes
     ///
     /// # Examples
     ///
@@ -934,8 +934,8 @@ pub const fn from_bits(v: u64) -> Self {
     /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as
     /// appropriate instead.
     ///
-    /// [`from_be_bytes`]: #method.from_be_bytes
-    /// [`from_le_bytes`]: #method.from_le_bytes
+    /// [`from_be_bytes`]: f64::from_be_bytes
+    /// [`from_le_bytes`]: f64::from_le_bytes
     ///
     /// # Examples
     ///
index 709ed26d2a39febf244e9634c1c45a6d827dc34b..ce9dca39d0ea4f7d7099d5cd143b2ae1f6333d61 100644 (file)
@@ -1067,7 +1067,7 @@ pub const fn wrapping_neg(self) -> Self {
         ///
         /// Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is restricted to
         /// the range of the type, rather than the bits shifted out of the LHS being returned to the other end.
-        /// The primitive integer types all implement a [`rotate_left`](#method.rotate_left) function,
+        /// The primitive integer types all implement a [`rotate_left`](Self::rotate_left) function,
         /// which may be what you want instead.
         ///
         /// # Examples
@@ -1096,7 +1096,7 @@ pub const fn wrapping_shl(self, rhs: u32) -> Self {
         ///
         /// Note that this is *not* the same as a rotate-right; the RHS of a wrapping shift-right is restricted
         /// to the range of the type, rather than the bits shifted out of the LHS being returned to the other
-        /// end. The primitive integer types all implement a [`rotate_right`](#method.rotate_right) function,
+        /// end. The primitive integer types all implement a [`rotate_right`](Self::rotate_right) function,
         /// which may be what you want instead.
         ///
         /// # Examples
@@ -1812,8 +1812,8 @@ pub const fn is_negative(self) -> bool { self < 0 }
         ///
         #[doc = $to_xe_bytes_doc]
         ///
-        /// [`to_be_bytes`]: #method.to_be_bytes
-        /// [`to_le_bytes`]: #method.to_le_bytes
+        /// [`to_be_bytes`]: Self::to_be_bytes
+        /// [`to_le_bytes`]: Self::to_le_bytes
         ///
         /// # Examples
         ///
@@ -1845,7 +1845,7 @@ pub const fn is_negative(self) -> bool { self < 0 }
         ///
         /// [`to_ne_bytes`] should be preferred over this whenever possible.
         ///
-        /// [`to_ne_bytes`]: #method.to_ne_bytes
+        /// [`to_ne_bytes`]: Self::to_ne_bytes
         ///
         /// # Examples
         ///
@@ -1937,8 +1937,8 @@ pub const fn is_negative(self) -> bool { self < 0 }
         /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as
         /// appropriate instead.
         ///
-        /// [`from_be_bytes`]: #method.from_be_bytes
-        /// [`from_le_bytes`]: #method.from_le_bytes
+        /// [`from_be_bytes`]: Self::from_be_bytes
+        /// [`from_le_bytes`]: Self::from_le_bytes
         ///
         #[doc = $to_xe_bytes_doc]
         ///
@@ -1976,7 +1976,7 @@ pub const fn is_negative(self) -> bool { self < 0 }
         }
 
         /// New code should prefer to use
-        #[doc = concat!("[`", stringify!($SelfT), "::MIN", "`](#associatedconstant.MIN).")]
+        #[doc = concat!("[`", stringify!($SelfT), "::MIN", "`] instead.")]
         ///
         /// Returns the smallest value that can be represented by this integer type.
         #[stable(feature = "rust1", since = "1.0.0")]
@@ -1989,7 +1989,7 @@ pub const fn min_value() -> Self {
         }
 
         /// New code should prefer to use
-        #[doc = concat!("[`", stringify!($SelfT), "::MAX", "`](#associatedconstant.MAX).")]
+        #[doc = concat!("[`", stringify!($SelfT), "::MAX", "`] instead.")]
         ///
         /// Returns the largest value that can be represented by this integer type.
         #[stable(feature = "rust1", since = "1.0.0")]
index c13f000a73615a3bc4c0868534e0bb300a96cd2d..f0bd976ba83d5599fc7c744f11ca5398f27d0a8a 100644 (file)
@@ -193,10 +193,11 @@ pub const fn is_ascii(&self) -> bool {
     /// assert_eq!(65, lowercase_a.to_ascii_uppercase());
     /// ```
     ///
-    /// [`make_ascii_uppercase`]: #method.make_ascii_uppercase
+    /// [`make_ascii_uppercase`]: Self::make_ascii_uppercase
     #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
+    #[rustc_const_stable(feature = "const_ascii_methods_on_intrinsics", since = "1.52.0")]
     #[inline]
-    pub fn to_ascii_uppercase(&self) -> u8 {
+    pub const fn to_ascii_uppercase(&self) -> u8 {
         // Unset the fifth bit if this is a lowercase letter
         *self & !((self.is_ascii_lowercase() as u8) * ASCII_CASE_MASK)
     }
@@ -216,17 +217,18 @@ pub fn to_ascii_uppercase(&self) -> u8 {
     /// assert_eq!(97, uppercase_a.to_ascii_lowercase());
     /// ```
     ///
-    /// [`make_ascii_lowercase`]: #method.make_ascii_lowercase
+    /// [`make_ascii_lowercase`]: Self::make_ascii_lowercase
     #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
+    #[rustc_const_stable(feature = "const_ascii_methods_on_intrinsics", since = "1.52.0")]
     #[inline]
-    pub fn to_ascii_lowercase(&self) -> u8 {
+    pub const fn to_ascii_lowercase(&self) -> u8 {
         // Set the fifth bit if this is an uppercase letter
         *self | (self.is_ascii_uppercase() as u8 * ASCII_CASE_MASK)
     }
 
     /// Assumes self is ascii
     #[inline]
-    pub(crate) fn ascii_change_case_unchecked(&self) -> u8 {
+    pub(crate) const fn ascii_change_case_unchecked(&self) -> u8 {
         *self ^ ASCII_CASE_MASK
     }
 
@@ -243,8 +245,9 @@ pub(crate) fn ascii_change_case_unchecked(&self) -> u8 {
     /// assert!(lowercase_a.eq_ignore_ascii_case(&uppercase_a));
     /// ```
     #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
+    #[rustc_const_stable(feature = "const_ascii_methods_on_intrinsics", since = "1.52.0")]
     #[inline]
-    pub fn eq_ignore_ascii_case(&self, other: &u8) -> bool {
+    pub const fn eq_ignore_ascii_case(&self, other: &u8) -> bool {
         self.to_ascii_lowercase() == other.to_ascii_lowercase()
     }
 
@@ -266,7 +269,7 @@ pub fn eq_ignore_ascii_case(&self, other: &u8) -> bool {
     /// assert_eq!(b'A', byte);
     /// ```
     ///
-    /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase
+    /// [`to_ascii_uppercase`]: Self::to_ascii_uppercase
     #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
     #[inline]
     pub fn make_ascii_uppercase(&mut self) {
@@ -291,7 +294,7 @@ pub fn make_ascii_uppercase(&mut self) {
     /// assert_eq!(b'a', byte);
     /// ```
     ///
-    /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase
+    /// [`to_ascii_lowercase`]: Self::to_ascii_lowercase
     #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
     #[inline]
     pub fn make_ascii_lowercase(&mut self) {
@@ -723,9 +726,6 @@ impl usize {
 /// This `enum` is used as the return type for [`f32::classify`] and [`f64::classify`]. See
 /// their documentation for more.
 ///
-/// [`f32::classify`]: ../../std/primitive.f32.html#method.classify
-/// [`f64::classify`]: ../../std/primitive.f64.html#method.classify
-///
 /// # Examples
 ///
 /// ```
index bf5331a01d58ea1413177f78b88a30cc9df10010..7393e7f923e1fdf5d9874a0610f8884814fc6f31 100644 (file)
@@ -994,7 +994,7 @@ pub const fn wrapping_neg(self) -> Self {
         /// RHS of a wrapping shift-left is restricted to the range
         /// of the type, rather than the bits shifted out of the LHS
         /// being returned to the other end. The primitive integer
-        /// types all implement a [`rotate_left`](#method.rotate_left) function,
+        /// types all implement a [`rotate_left`](Self::rotate_left) function,
         /// which may be what you want instead.
         ///
         /// # Examples
@@ -1026,7 +1026,7 @@ pub const fn wrapping_shl(self, rhs: u32) -> Self {
         /// RHS of a wrapping shift-right is restricted to the range
         /// of the type, rather than the bits shifted out of the LHS
         /// being returned to the other end. The primitive integer
-        /// types all implement a [`rotate_right`](#method.rotate_right) function,
+        /// types all implement a [`rotate_right`](Self::rotate_right) function,
         /// which may be what you want instead.
         ///
         /// # Examples
@@ -1642,8 +1642,8 @@ pub const fn wrapping_next_power_of_two(self) -> Self {
         ///
         #[doc = $to_xe_bytes_doc]
         ///
-        /// [`to_be_bytes`]: #method.to_be_bytes
-        /// [`to_le_bytes`]: #method.to_le_bytes
+        /// [`to_be_bytes`]: Self::to_be_bytes
+        /// [`to_le_bytes`]: Self::to_le_bytes
         ///
         /// # Examples
         ///
@@ -1675,7 +1675,7 @@ pub const fn wrapping_next_power_of_two(self) -> Self {
         ///
         /// [`to_ne_bytes`] should be preferred over this whenever possible.
         ///
-        /// [`to_ne_bytes`]: #method.to_ne_bytes
+        /// [`to_ne_bytes`]: Self::to_ne_bytes
         ///
         /// # Examples
         ///
@@ -1767,8 +1767,8 @@ pub const fn wrapping_next_power_of_two(self) -> Self {
         /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as
         /// appropriate instead.
         ///
-        /// [`from_be_bytes`]: #method.from_be_bytes
-        /// [`from_le_bytes`]: #method.from_le_bytes
+        /// [`from_be_bytes`]: Self::from_be_bytes
+        /// [`from_le_bytes`]: Self::from_le_bytes
         ///
         #[doc = $from_xe_bytes_doc]
         ///
@@ -1806,8 +1806,7 @@ pub const fn wrapping_next_power_of_two(self) -> Self {
         }
 
         /// New code should prefer to use
-        #[doc = concat!("[`", stringify!($SelfT), "::MIN", "`](#associatedconstant.MIN).")]
-        /// instead.
+        #[doc = concat!("[`", stringify!($SelfT), "::MIN", "`] instead.")]
         ///
         /// Returns the smallest value that can be represented by this integer type.
         #[stable(feature = "rust1", since = "1.0.0")]
@@ -1818,8 +1817,7 @@ pub const fn wrapping_next_power_of_two(self) -> Self {
         pub const fn min_value() -> Self { Self::MIN }
 
         /// New code should prefer to use
-        #[doc = concat!("[`", stringify!($SelfT), "::MAX", "`](#associatedconstant.MAX).")]
-        /// instead.
+        #[doc = concat!("[`", stringify!($SelfT), "::MAX", "`] instead.")]
         ///
         /// Returns the largest value that can be represented by this integer type.
         #[stable(feature = "rust1", since = "1.0.0")]
index 14e4e4da3b96dba97faa730aef9cb04391435b5c..bcd2b207c4f6f0c2b4bd335e59c324cb5378492f 100644 (file)
@@ -336,7 +336,7 @@ pub fn as_pin_mut(self: Pin<&mut Self>) -> Option<Pin<&mut T>> {
     /// assert_eq!(x.expect("fruits are healthy"), "value");
     /// ```
     ///
-    /// ```{.should_panic}
+    /// ```should_panic
     /// let x: Option<&str> = None;
     /// x.expect("fruits are healthy"); // panics with `fruits are healthy`
     /// ```
@@ -372,7 +372,7 @@ pub fn expect(self, msg: &str) -> T {
     /// assert_eq!(x.unwrap(), "air");
     /// ```
     ///
-    /// ```{.should_panic}
+    /// ```should_panic
     /// let x: Option<&str> = None;
     /// assert_eq!(x.unwrap(), "air"); // fails
     /// ```
@@ -1114,7 +1114,7 @@ impl<T: fmt::Debug> Option<T> {
     /// }
     /// ```
     ///
-    /// ```{.should_panic}
+    /// ```should_panic
     /// #![feature(option_expect_none)]
     ///
     /// use std::collections::HashMap;
@@ -1156,7 +1156,7 @@ pub fn expect_none(self, msg: &str) {
     /// }
     /// ```
     ///
-    /// ```{.should_panic}
+    /// ```should_panic
     /// #![feature(option_unwrap_none)]
     ///
     /// use std::collections::HashMap;
index 9c53430ce3556319da81f9619f757ca3f9e63956..481d5d772b4a91b839cb78b2dcdb955f3b45b77f 100644 (file)
@@ -902,7 +902,8 @@ pub unsafe fn replace<T>(dst: *mut T, mut src: T) -> T {
 /// ```
 #[inline]
 #[stable(feature = "rust1", since = "1.0.0")]
-pub unsafe fn write<T>(dst: *mut T, src: T) {
+#[rustc_const_unstable(feature = "const_ptr_write", issue = "none")]
+pub const unsafe fn write<T>(dst: *mut T, src: T) {
     // SAFETY: the caller must guarantee that `dst` is valid for writes.
     // `dst` cannot overlap `src` because the caller has mutable access
     // to `dst` while `src` is owned by this function.
@@ -998,14 +999,16 @@ pub unsafe fn write<T>(dst: *mut T, src: T) {
 /// ```
 #[inline]
 #[stable(feature = "ptr_unaligned", since = "1.17.0")]
-pub unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
+#[rustc_const_unstable(feature = "const_ptr_write", issue = "none")]
+pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
     // SAFETY: the caller must guarantee that `dst` is valid for writes.
     // `dst` cannot overlap `src` because the caller has mutable access
     // to `dst` while `src` is owned by this function.
     unsafe {
         copy_nonoverlapping(&src as *const T as *const u8, dst as *mut u8, mem::size_of::<T>());
+        // We are calling the intrinsic directly to avoid function calls in the generated code.
+        intrinsics::forget(src);
     }
-    mem::forget(src);
 }
 
 /// Performs a volatile read of the value from `src` without moving it. This
index 6651c3dd4e86bd90dd2657870f548f4c918411fb..4e3e88b946cd9311bdfbce391f3c975e5698a7bd 100644 (file)
@@ -1003,8 +1003,9 @@ pub unsafe fn drop_in_place(self) {
     ///
     /// [`ptr::write`]: crate::ptr::write()
     #[stable(feature = "pointer_methods", since = "1.26.0")]
+    #[rustc_const_unstable(feature = "const_ptr_write", issue = "none")]
     #[inline]
-    pub unsafe fn write(self, val: T)
+    pub const unsafe fn write(self, val: T)
     where
         T: Sized,
     {
@@ -1057,8 +1058,9 @@ pub unsafe fn write_volatile(self, val: T)
     ///
     /// [`ptr::write_unaligned`]: crate::ptr::write_unaligned()
     #[stable(feature = "pointer_methods", since = "1.26.0")]
+    #[rustc_const_unstable(feature = "const_ptr_write", issue = "none")]
     #[inline]
-    pub unsafe fn write_unaligned(self, val: T)
+    pub const unsafe fn write_unaligned(self, val: T)
     where
         T: Sized,
     {
index d8747f8b8d6dcb4900af1f98325333cc47cf8586..c7121c7ee595466343533820a2681e4ac2c39fa2 100644 (file)
 //! assert success with [`expect`]. This will panic if the
 //! write fails, providing a marginally useful message indicating why:
 //!
-//! ```{.no_run}
+//! ```no_run
 //! use std::fs::File;
 //! use std::io::prelude::*;
 //!
 //!
 //! You might also simply assert success:
 //!
-//! ```{.no_run}
+//! ```no_run
 //! # use std::fs::File;
 //! # use std::io::prelude::*;
 //! # let mut file = File::create("valuable_data.txt").unwrap();
@@ -984,7 +984,7 @@ impl<T, E: fmt::Debug> Result<T, E> {
     ///
     /// Basic usage:
     ///
-    /// ```{.should_panic}
+    /// ```should_panic
     /// let x: Result<u32, &str> = Err("emergency failure");
     /// x.expect("Testing expect"); // panics with `Testing expect: emergency failure`
     /// ```
@@ -1024,7 +1024,7 @@ pub fn expect(self, msg: &str) -> T {
     /// assert_eq!(x.unwrap(), 2);
     /// ```
     ///
-    /// ```{.should_panic}
+    /// ```should_panic
     /// let x: Result<u32, &str> = Err("emergency failure");
     /// x.unwrap(); // panics with `emergency failure`
     /// ```
@@ -1052,7 +1052,7 @@ impl<T: fmt::Debug, E> Result<T, E> {
     ///
     /// Basic usage:
     ///
-    /// ```{.should_panic}
+    /// ```should_panic
     /// let x: Result<u32, &str> = Ok(10);
     /// x.expect_err("Testing expect_err"); // panics with `Testing expect_err: 10`
     /// ```
@@ -1075,7 +1075,7 @@ pub fn expect_err(self, msg: &str) -> E {
     ///
     /// # Examples
     ///
-    /// ```{.should_panic}
+    /// ```should_panic
     /// let x: Result<u32, &str> = Ok(2);
     /// x.unwrap_err(); // panics with `2`
     /// ```
index 4acd059ab03dfeaab4b80dd7338e3c5330e9eced..152fed803ecdb751167ccbb31a3155958920523a 100644 (file)
@@ -49,3 +49,53 @@ fn mut_ptr_read() {
     const UNALIGNED: u16 = unsafe { UNALIGNED_PTR.read_unaligned() };
     assert_eq!(UNALIGNED, u16::from_ne_bytes([0x23, 0x45]));
 }
+
+#[test]
+fn write() {
+    use core::ptr;
+
+    const fn write_aligned() -> i32 {
+        let mut res = 0;
+        unsafe {
+            ptr::write(&mut res as *mut _, 42);
+        }
+        res
+    }
+    const ALIGNED: i32 = write_aligned();
+    assert_eq!(ALIGNED, 42);
+
+    const fn write_unaligned() -> [u16; 2] {
+        let mut two_aligned = [0u16; 2];
+        unsafe {
+            let unaligned_ptr = (two_aligned.as_mut_ptr() as *mut u8).add(1) as *mut u16;
+            ptr::write_unaligned(unaligned_ptr, u16::from_ne_bytes([0x23, 0x45]));
+        }
+        two_aligned
+    }
+    const UNALIGNED: [u16; 2] = write_unaligned();
+    assert_eq!(UNALIGNED, [u16::from_ne_bytes([0x00, 0x23]), u16::from_ne_bytes([0x45, 0x00])]);
+}
+
+#[test]
+fn mut_ptr_write() {
+    const fn aligned() -> i32 {
+        let mut res = 0;
+        unsafe {
+            (&mut res as *mut i32).write(42);
+        }
+        res
+    }
+    const ALIGNED: i32 = aligned();
+    assert_eq!(ALIGNED, 42);
+
+    const fn write_unaligned() -> [u16; 2] {
+        let mut two_aligned = [0u16; 2];
+        unsafe {
+            let unaligned_ptr = (two_aligned.as_mut_ptr() as *mut u8).add(1) as *mut u16;
+            unaligned_ptr.write_unaligned(u16::from_ne_bytes([0x23, 0x45]));
+        }
+        two_aligned
+    }
+    const UNALIGNED: [u16; 2] = write_unaligned();
+    assert_eq!(UNALIGNED, [u16::from_ne_bytes([0x00, 0x23]), u16::from_ne_bytes([0x45, 0x00])]);
+}
index 9692724545f289708789aad480b63dee63f10e63..d6d3111e2ffa71f267a4e2de11271ebd66da3a90 100644 (file)
@@ -14,6 +14,7 @@
 #![feature(const_cell_into_inner)]
 #![feature(const_maybe_uninit_assume_init)]
 #![feature(const_ptr_read)]
+#![feature(const_ptr_write)]
 #![feature(const_ptr_offset)]
 #![feature(control_flow_enum)]
 #![feature(core_intrinsics)]
index 605d953f5da7130add600f95f886b3de1c0675a1..94338c7b04d0634a739f9fb2e1e848b7bb224a44 100644 (file)
@@ -30,6 +30,7 @@
 use crate::num;
 use crate::str;
 use crate::string;
+use crate::sync::Arc;
 
 /// `Error` is a trait representing the basic expectations for error values,
 /// i.e., values of type `E` in [`Result<T, E>`]. Errors must describe
@@ -507,6 +508,27 @@ fn backtrace(&self) -> Option<&Backtrace> {
     }
 }
 
+#[stable(feature = "arc_error", since = "1.52.0")]
+impl<T: Error + ?Sized> Error for Arc<T> {
+    #[allow(deprecated, deprecated_in_future)]
+    fn description(&self) -> &str {
+        Error::description(&**self)
+    }
+
+    #[allow(deprecated)]
+    fn cause(&self) -> Option<&dyn Error> {
+        Error::cause(&**self)
+    }
+
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        Error::source(&**self)
+    }
+
+    fn backtrace(&self) -> Option<&Backtrace> {
+        Error::backtrace(&**self)
+    }
+}
+
 #[stable(feature = "fmt_error", since = "1.11.0")]
 impl Error for fmt::Error {
     #[allow(deprecated)]
index 3780f2044cb9039659c0d0262a78e4b6c9210e12..eb60df214c489cff2d420e15b68f811cdbdd2d22 100644 (file)
@@ -106,7 +106,7 @@ fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64> {
                     Ok(0) => return Ok(len), // EOF reached
                     Ok(bytes_read) => {
                         assert!(bytes_read <= spare_cap.len());
-                        // Safety: The initializer contract guarantees that either it or `read`
+                        // SAFETY: The initializer contract guarantees that either it or `read`
                         // will have initialized these bytes. And we just checked that the number
                         // of bytes is within the buffer capacity.
                         unsafe { buf.set_len(buf.len() + bytes_read) };
index 68f57958bb233c5c8f39eaca8de07ad67135674d..aec2a2aa639edc799f2d87fff6535e595a984dff 100644 (file)
@@ -440,13 +440,17 @@ fn initialize<F, E>(&self, f: F) -> Result<(), E>
         res
     }
 
-    /// Safety: The value must be initialized
+    /// # Safety
+    ///
+    /// The value must be initialized
     unsafe fn get_unchecked(&self) -> &T {
         debug_assert!(self.is_initialized());
         (&*self.value.get()).assume_init_ref()
     }
 
-    /// Safety: The value must be initialized
+    /// # Safety
+    ///
+    /// The value must be initialized
     unsafe fn get_unchecked_mut(&mut self) -> &mut T {
         debug_assert!(self.is_initialized());
         (&mut *self.value.get()).assume_init_mut()
@@ -456,7 +460,7 @@ unsafe fn get_unchecked_mut(&mut self) -> &mut T {
 unsafe impl<#[may_dangle] T> Drop for SyncOnceCell<T> {
     fn drop(&mut self) {
         if self.is_initialized() {
-            // Safety: The cell is initialized and being dropped, so it can't
+            // SAFETY: The cell is initialized and being dropped, so it can't
             // be accessed again. We also don't touch the `T` other than
             // dropping it, which validates our usage of #[may_dangle].
             unsafe { (&mut *self.value.get()).assume_init_drop() };
index 588bffb57c9c5925f91f6188bfff505b898de93d..32aca8c83924d939ae4f4b1e09ce6ef2c8dfe4dc 100644 (file)
 #![feature(exhaustive_patterns)]
 #![feature(extend_one)]
 #![feature(extended_key_value_attributes)]
-#![feature(external_doc)]
 #![feature(fn_traits)]
 #![feature(format_args_nl)]
 #![feature(gen_future)]
index c0750f8c0d1b03ab3c180ee360d94d90dcd2d5e6..b729349cf530f411adced32a03f01457b2531c9a 100644 (file)
@@ -4,7 +4,7 @@
 //! library. Each macro is available for use when linking against the standard
 //! library.
 
-#[doc(include = "../../core/src/macros/panic.md")]
+#[doc = include_str!("../../core/src/macros/panic.md")]
 #[macro_export]
 #[rustc_builtin_macro = "std_panic"]
 #[stable(feature = "rust1", since = "1.0.0")]
index 22c98d7ade99240e3a0b5961f84ec597c69c82d9..50464a050c70688b0112df517e90aef932329d62 100644 (file)
@@ -18,7 +18,7 @@ macro_rules! type_alias_no_nz {
       $Docfile:tt, $Alias:ident = $Real:ty;
       $( $Cfg:tt )*
     } => {
-        #[doc(include = $Docfile)]
+        #[doc = include_str!($Docfile)]
         $( $Cfg )*
         #[stable(feature = "raw_os", since = "1.1.0")]
         pub type $Alias = $Real;
index 7559c1f1d9e290d41ef4be7d0baeca3896787a46..88a27f27f6628e866cf620e7da76a440af0defc9 100644 (file)
@@ -172,6 +172,8 @@ unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
     }
 
     fn exec(&mut self) -> io::Error {
+        // NOTE: This may *not* be safe to call after `libc::fork`, because it
+        // may allocate. That may be worth fixing at some point in the future.
         self.as_inner_mut().exec(sys::process::Stdio::Inherit)
     }
 
index a96d4aa6a4555c2d8def7895b654fb057428679f..b9dcc4e4b9e381c13b078e55781c1a7f8946a573 100644 (file)
@@ -60,25 +60,13 @@ pub unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::
 ////////////////////////////////////////////////////////////////////////////////
 
 pub struct Command {
-    // Currently we try hard to ensure that the call to `.exec()` doesn't
-    // actually allocate any memory. While many platforms try to ensure that
-    // memory allocation works after a fork in a multithreaded process, it's
-    // been observed to be buggy and somewhat unreliable, so we do our best to
-    // just not do it at all!
-    //
-    // Along those lines, the `argv` and `envp` raw pointers here are exactly
-    // what's gonna get passed to `execvp`. The `argv` array starts with the
-    // `program` and ends with a NULL, and the `envp` pointer, if present, is
-    // also null-terminated.
-    //
-    // Right now we don't support removing arguments, so there's no much fancy
-    // support there, but we support adding and removing environment variables,
-    // so a side table is used to track where in the `envp` array each key is
-    // located. Whenever we add a key we update it in place if it's already
-    // present, and whenever we remove a key we update the locations of all
-    // other keys.
     program: CString,
     args: Vec<CString>,
+    /// Exactly what will be passed to `execvp`.
+    ///
+    /// First element is a pointer to `program`, followed by pointers to
+    /// `args`, followed by a `null`. Be careful when modifying `program` or
+    /// `args` to properly update this as well.
     argv: Argv,
     env: CommandEnv,
 
index c10c0df4a3a9908e8aaddf7e4dda86be2aa68724..b8f512f6a232fc88fdf64fd3b2eb05fa1f3441f6 100644 (file)
@@ -8,7 +8,9 @@
 pub const MAIN_SEP_STR: &str = "\\";
 pub const MAIN_SEP: char = '\\';
 
-// Safety: `bytes` must be a valid wtf8 encoded slice
+/// # Safety
+///
+/// `bytes` must be a valid wtf8 encoded slice
 #[inline]
 unsafe fn bytes_as_os_str(bytes: &[u8]) -> &OsStr {
     // &OsStr is layout compatible with &Slice, which is compatible with &Wtf8,
@@ -130,7 +132,7 @@ fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) {
             // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index.
             let path = &path.bytes()[separator_end..];
 
-            // Safety: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\')
+            // SAFETY: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\')
             // is encoded in a single byte, therefore `bytes[separator_start]` and
             // `bytes[separator_end]` must be code point boundaries and thus
             // `bytes[..separator_start]` and `bytes[separator_end..]` are valid wtf8 slices.
index 41e8ad772946388da8fafb48f1ecf66bfbc31d7b..70b31b19f824cb16eda7030a235fc7de88d3f4c5 100644 (file)
@@ -103,7 +103,7 @@ pub const fn new() -> StaticRWLock {
     /// The lock is automatically unlocked when the returned guard is dropped.
     #[inline]
     pub fn read_with_guard(&'static self) -> RWLockReadGuard {
-        // Safety: All methods require static references, therefore self
+        // SAFETY: All methods require static references, therefore self
         // cannot be moved between invocations.
         unsafe {
             self.0.read();
@@ -117,7 +117,7 @@ pub fn read_with_guard(&'static self) -> RWLockReadGuard {
     /// The lock is automatically unlocked when the returned guard is dropped.
     #[inline]
     pub fn write_with_guard(&'static self) -> RWLockWriteGuard {
-        // Safety: All methods require static references, therefore self
+        // SAFETY: All methods require static references, therefore self
         // cannot be moved between invocations.
         unsafe {
             self.0.write();
index 4f7a101d2acbd09a30404b845a46691b16bbb2a2..29a267053b47d109e1f3d68154104701ee802e22 100644 (file)
@@ -286,8 +286,8 @@ achieve that will result in false positive reports.
 
 Detecting the use of uninitialized memory. The `-Zbuild-std` flag rebuilds and
 instruments the standard library, and is strictly necessary for the correct
-operation of the tool. The `-Zsanitizer-track-origins` enables tracking of the
-origins of uninitialized memory:
+operation of the tool. The `-Zsanitizer-memory-track-origins` enables tracking
+of the origins of uninitialized memory:
 
 ```rust
 use std::mem::MaybeUninit;
index d44c841d48c66bc5aa815a21800908dec56ea3db..22780804610b6365821d0bb67ad621912db541fd 100644 (file)
@@ -66,7 +66,7 @@ Other features provided by lang items include:
   marked with lang items; those specific four are `eq`, `ord`,
   `deref`, and `add` respectively.
 - stack unwinding and general failure; the `eh_personality`,
-  `panic` and `panic_bounds_checks` lang items.
+  `panic` and `panic_bounds_check` lang items.
 - the traits in `std::marker` used to indicate types of
   various kinds; lang items `send`, `sync` and `copy`.
 - the marker types and variance indicators found in
index 70b4e9d9908c4d5083532cecc9e5088c96929e10..9045adb54dc1e425bb9825b85c867799515f30fe 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/env bash
 #
 # Call `tidy --bless` before each commit
-# Copy this scripts to .git/hooks to activate,
+# Copy this script to .git/hooks to activate,
 # and remove it from .git/hooks to deactivate.
 #
 
index f9c63186544a4d3f1275ee3de0d5dcc680c13be4..9cfa72132cb5a99aae5f2404d79d77baa81e0577 100644 (file)
@@ -414,7 +414,10 @@ fn merge_attrs(
 
     debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());
 
-    let mut item = clean::Item::from_def_id_and_parts(
+    let attrs = box merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs);
+    debug!("merged_attrs={:?}", attrs);
+
+    ret.push(clean::Item::from_def_id_and_attrs_and_parts(
         did,
         None,
         clean::ImplItem(clean::Impl {
@@ -428,11 +431,9 @@ fn merge_attrs(
             synthetic: false,
             blanket_impl: None,
         }),
+        attrs,
         cx,
-    );
-    item.attrs = box merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs);
-    debug!("merged_attrs={:?}", item.attrs);
-    ret.push(item);
+    ));
 }
 
 fn build_module(
index 51bef344e6722a80186032cfedec372e67e528d3..7fc48cd5f0b38819f37882593469c493f7fbcf45 100644 (file)
@@ -147,6 +147,22 @@ pub fn from_def_id_and_parts(
         name: Option<Symbol>,
         kind: ItemKind,
         cx: &mut DocContext<'_>,
+    ) -> Item {
+        Self::from_def_id_and_attrs_and_parts(
+            def_id,
+            name,
+            kind,
+            box cx.tcx.get_attrs(def_id).clean(cx),
+            cx,
+        )
+    }
+
+    pub fn from_def_id_and_attrs_and_parts(
+        def_id: DefId,
+        name: Option<Symbol>,
+        kind: ItemKind,
+        attrs: Box<Attributes>,
+        cx: &mut DocContext<'_>,
     ) -> Item {
         debug!("name={:?}, def_id={:?}", name, def_id);
 
@@ -164,7 +180,7 @@ pub fn from_def_id_and_parts(
             kind: box kind,
             name,
             source: source.clean(cx),
-            attrs: box cx.tcx.get_attrs(def_id).clean(cx),
+            attrs,
             visibility: cx.tcx.visibility(def_id).clean(cx),
         }
     }
index cb11f22d0d8593afe0c7c022066bb4f536b3a733..9a054e29dd3b5929e6a4d6a4d8df882f74effb0d 100644 (file)
@@ -780,6 +780,31 @@ fn parse_without_check(
         Self::parse(string, allow_error_code_check, enable_per_target_ignores, None)
     }
 
+    fn tokens(string: &str) -> impl Iterator<Item = &str> {
+        // Pandoc, which Rust once used for generating documentation,
+        // expects lang strings to be surrounded by `{}` and for each token
+        // to be proceeded by a `.`. Since some of these lang strings are still
+        // loose in the wild, we strip a pair of surrounding `{}` from the lang
+        // string and a leading `.` from each token.
+
+        let string = string.trim();
+
+        let first = string.chars().next();
+        let last = string.chars().last();
+
+        let string = if first == Some('{') && last == Some('}') {
+            &string[1..string.len() - 1]
+        } else {
+            string
+        };
+
+        string
+            .split(|c| c == ',' || c == ' ' || c == '\t')
+            .map(str::trim)
+            .map(|token| if token.chars().next() == Some('.') { &token[1..] } else { token })
+            .filter(|token| !token.is_empty())
+    }
+
     fn parse(
         string: &str,
         allow_error_code_check: ErrorCodes,
@@ -793,11 +818,11 @@ fn parse(
         let mut ignores = vec![];
 
         data.original = string.to_owned();
-        let tokens = string.split(|c: char| !(c == '_' || c == '-' || c.is_alphanumeric()));
+
+        let tokens = Self::tokens(string).collect::<Vec<&str>>();
 
         for token in tokens {
-            match token.trim() {
-                "" => {}
+            match token {
                 "should_panic" => {
                     data.should_panic = true;
                     seen_rust_tags = !seen_other_tags;
@@ -894,6 +919,7 @@ fn parse(
                 _ => seen_other_tags = true,
             }
         }
+
         // ignore-foo overrides ignore
         if !ignores.is_empty() {
             data.ignore = Ignore::Some(ignores);
index 6b2cfe685752da452a8452f7f3ddadf1259954e5..59ca841715c59c07302244de741821a11ee093e9 100644 (file)
@@ -58,6 +58,9 @@ fn t(lg: LangString) {
 
     t(Default::default());
     t(LangString { original: "rust".into(), ..Default::default() });
+    t(LangString { original: ".rust".into(), ..Default::default() });
+    t(LangString { original: "{rust}".into(), ..Default::default() });
+    t(LangString { original: "{.rust}".into(), ..Default::default() });
     t(LangString { original: "sh".into(), rust: false, ..Default::default() });
     t(LangString { original: "ignore".into(), ignore: Ignore::All, ..Default::default() });
     t(LangString {
@@ -75,16 +78,16 @@ fn t(lg: LangString) {
         ..Default::default()
     });
     t(LangString { original: "allow_fail".into(), allow_fail: true, ..Default::default() });
-    t(LangString { original: "{.no_run .example}".into(), no_run: true, ..Default::default() });
+    t(LangString { original: "no_run,example".into(), no_run: true, ..Default::default() });
     t(LangString {
-        original: "{.sh .should_panic}".into(),
+        original: "sh,should_panic".into(),
         should_panic: true,
         rust: false,
         ..Default::default()
     });
-    t(LangString { original: "{.example .rust}".into(), ..Default::default() });
+    t(LangString { original: "example,rust".into(), ..Default::default() });
     t(LangString {
-        original: "{.test_harness .rust}".into(),
+        original: "test_harness,.rust".into(),
         test_harness: true,
         ..Default::default()
     });
@@ -100,6 +103,18 @@ fn t(lg: LangString) {
         rust: false,
         ..Default::default()
     });
+    t(LangString {
+        original: "text,no_run, ".into(),
+        no_run: true,
+        rust: false,
+        ..Default::default()
+    });
+    t(LangString {
+        original: "text,no_run,".into(),
+        no_run: true,
+        rust: false,
+        ..Default::default()
+    });
     t(LangString {
         original: "edition2015".into(),
         edition: Some(Edition::Edition2015),
@@ -112,6 +127,29 @@ fn t(lg: LangString) {
     });
 }
 
+#[test]
+fn test_lang_string_tokenizer() {
+    fn case(lang_string: &str, want: &[&str]) {
+        let have = LangString::tokens(lang_string).collect::<Vec<&str>>();
+        assert_eq!(have, want, "Unexpected lang string split for `{}`", lang_string);
+    }
+
+    case("", &[]);
+    case("foo", &["foo"]);
+    case("foo,bar", &["foo", "bar"]);
+    case(".foo,.bar", &["foo", "bar"]);
+    case("{.foo,.bar}", &["foo", "bar"]);
+    case("  {.foo,.bar}  ", &["foo", "bar"]);
+    case("foo bar", &["foo", "bar"]);
+    case("foo\tbar", &["foo", "bar"]);
+    case("foo\t, bar", &["foo", "bar"]);
+    case(" foo , bar ", &["foo", "bar"]);
+    case(",,foo,,bar,,", &["foo", "bar"]);
+    case("foo=bar", &["foo=bar"]);
+    case("a-b-c", &["a-b-c"]);
+    case("a_b_c", &["a_b_c"]);
+}
+
 #[test]
 fn test_header() {
     fn t(input: &str, expect: &str) {
index b21f6a13392361bd82f5ba20d3903a93a1f8a1f2..6665f160dec8a2933469174665c1246fe3178417 100644 (file)
@@ -1343,7 +1343,6 @@ fn print_entries(f: &mut Buffer, e: &FxHashSet<ItemEntry>, title: &str, class: &
                          </a>\
                      </span>
                  </span>
-                 <span class=\"in-band\">List of all items</span>\
              </h1>",
         );
         // Note: print_entries does not escape the title, because we know the current set of titles
index abf5f05fe58ab71e7f553f4a276278efbf6a1088..224c794fb3b4a035cc8ad077756a0f8d1c5a4102 100644 (file)
@@ -38,3 +38,14 @@ fn test_name_sorting() {
     sorted.sort_by(|&l, r| compare_names(l, r));
     assert_eq!(names, sorted);
 }
+
+#[test]
+fn test_all_types_prints_header_once() {
+    // Regression test for #82477
+    let all_types = AllTypes::new();
+
+    let mut buffer = Buffer::new();
+    all_types.print(&mut buffer);
+
+    assert_eq!(1, buffer.into_inner().matches("List of all items").count());
+}
index 0e0426279183f631fd297c1d4b4bb0c65b475457..da9a75e3e85e927157a1f1a9b706614940939e98 100644 (file)
@@ -1,2 +1,2 @@
 /* ignore-tidy-linelength */
-/*! normalize.css v3.0.0 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}
index c99e1ecac739c207fa049ebd0a6c36d1a098f777..6d9e3d0b9eab0d6cd1f5aa80ea07173d046c2c63 100644 (file)
@@ -134,15 +134,20 @@ fn try_from(res: ResolveRes) -> Result<Self, ()> {
     }
 }
 
-#[derive(Debug)]
 /// A link failed to resolve.
+#[derive(Debug)]
 enum ResolutionFailure<'a> {
     /// This resolved, but with the wrong namespace.
-    ///
-    /// `Namespace` is the namespace specified with a disambiguator
-    /// (as opposed to the actual namespace of the `Res`).
-    WrongNamespace(Res, /* disambiguated */ Namespace),
-    /// The link failed to resolve. `resolution_failure` should look to see if there's
+    WrongNamespace {
+        /// What the link resolved to.
+        res: Res,
+        /// The expected namespace for the resolution, determined from the link's disambiguator.
+        ///
+        /// E.g., for `[fn@Result]` this is [`Namespace::ValueNS`],
+        /// even though `Result`'s actual namespace is [`Namespace::TypeNS`].
+        expected_ns: Namespace,
+    },
+    /// The link failed to resolve. [`resolution_failure`] should look to see if there's
     /// a more helpful error that can be given.
     NotResolved {
         /// The scope the link was resolved in.
@@ -157,12 +162,11 @@ enum ResolutionFailure<'a> {
         unresolved: Cow<'a, str>,
     },
     /// This happens when rustdoc can't determine the parent scope for an item.
-    ///
     /// It is always a bug in rustdoc.
     NoParentItem,
     /// This link has malformed generic parameters; e.g., the angle brackets are unbalanced.
     MalformedGenerics(MalformedGenerics),
-    /// Used to communicate that this should be ignored, but shouldn't be reported to the user
+    /// Used to communicate that this should be ignored, but shouldn't be reported to the user.
     ///
     /// This happens when there is no disambiguator and one of the namespaces
     /// failed to resolve.
@@ -216,7 +220,7 @@ impl ResolutionFailure<'a> {
     /// Returns the full resolution of the link, if present.
     fn full_res(&self) -> Option<Res> {
         match self {
-            Self::WrongNamespace(res, _) => Some(*res),
+            Self::WrongNamespace { res, expected_ns: _ } => Some(*res),
             _ => None,
         }
     }
@@ -1308,20 +1312,20 @@ fn resolve_with_disambiguator(
         let extra_fragment = &key.extra_fragment;
 
         match disambiguator.map(Disambiguator::ns) {
-            Some(ns @ (ValueNS | TypeNS)) => {
-                match self.resolve(path_str, ns, base_node, extra_fragment) {
+            Some(expected_ns @ (ValueNS | TypeNS)) => {
+                match self.resolve(path_str, expected_ns, base_node, extra_fragment) {
                     Ok(res) => Some(res),
                     Err(ErrorKind::Resolve(box mut kind)) => {
                         // We only looked in one namespace. Try to give a better error if possible.
                         if kind.full_res().is_none() {
-                            let other_ns = if ns == ValueNS { TypeNS } else { ValueNS };
+                            let other_ns = if expected_ns == ValueNS { TypeNS } else { ValueNS };
                             // FIXME: really it should be `resolution_failure` that does this, not `resolve_with_disambiguator`
                             // See https://github.com/rust-lang/rust/pull/76955#discussion_r493953382 for a good approach
                             for &new_ns in &[other_ns, MacroNS] {
                                 if let Some(res) =
                                     self.check_full_res(new_ns, path_str, base_node, extra_fragment)
                                 {
-                                    kind = ResolutionFailure::WrongNamespace(res, ns);
+                                    kind = ResolutionFailure::WrongNamespace { res, expected_ns };
                                     break;
                                 }
                             }
@@ -1396,7 +1400,7 @@ fn resolve_with_disambiguator(
                         // Constructors are picked up in the type namespace.
                         match res {
                             Res::Def(DefKind::Ctor(..), _) => {
-                                Err(ResolutionFailure::WrongNamespace(res, TypeNS))
+                                Err(ResolutionFailure::WrongNamespace { res, expected_ns: TypeNS })
                             }
                             _ => {
                                 match (fragment, extra_fragment.clone()) {
@@ -1457,7 +1461,8 @@ fn resolve_with_disambiguator(
                             if let Some(res) =
                                 self.check_full_res(ns, path_str, base_node, extra_fragment)
                             {
-                                kind = ResolutionFailure::WrongNamespace(res, MacroNS);
+                                kind =
+                                    ResolutionFailure::WrongNamespace { res, expected_ns: MacroNS };
                                 break;
                             }
                         }
@@ -1889,7 +1894,7 @@ fn split(path: &str) -> Option<(&str, &str)> {
                 let note = match failure {
                     ResolutionFailure::NotResolved { .. } => unreachable!("handled above"),
                     ResolutionFailure::Dummy => continue,
-                    ResolutionFailure::WrongNamespace(res, expected_ns) => {
+                    ResolutionFailure::WrongNamespace { res, expected_ns } => {
                         if let Res::Def(kind, _) = res {
                             let disambiguator = Disambiguator::Kind(kind);
                             suggest_disambiguator(
@@ -1910,7 +1915,7 @@ fn split(path: &str) -> Option<(&str, &str)> {
                     }
                     ResolutionFailure::NoParentItem => {
                         diag.level = rustc_errors::Level::Bug;
-                        "all intra doc links should have a parent item".to_owned()
+                        "all intra-doc links should have a parent item".to_owned()
                     }
                     ResolutionFailure::MalformedGenerics(variant) => match variant {
                         MalformedGenerics::UnbalancedAngleBrackets => {
diff --git a/src/test/ui/auxiliary/legacy-const-generics.rs b/src/test/ui/auxiliary/legacy-const-generics.rs
new file mode 100644 (file)
index 0000000..67352a2
--- /dev/null
@@ -0,0 +1,6 @@
+#![feature(rustc_attrs)]
+
+#[rustc_legacy_const_generics(1)]
+pub fn foo<const Y: usize>(x: usize, z: usize) -> [usize; 3] {
+    [x, Y, z]
+}
diff --git a/src/test/ui/const-generics/conservative_is_privately_uninhabited_uses_correct_param_env-1.rs b/src/test/ui/const-generics/conservative_is_privately_uninhabited_uses_correct_param_env-1.rs
new file mode 100644 (file)
index 0000000..d1bffae
--- /dev/null
@@ -0,0 +1,27 @@
+// run-pass
+#![feature(const_generics, const_evaluatable_checked)]
+#![allow(incomplete_features)]
+
+// This tests that the `conservative_is_privately_uninhabited` fn doesn't cause
+// ICEs by trying to evaluate `T::ASSOC` with an incorrect `ParamEnv`.
+
+trait Foo {
+    const ASSOC: usize = 1;
+}
+
+struct Iced<T: Foo>(T, [(); T::ASSOC])
+where
+    [(); T::ASSOC]: ;
+
+impl Foo for u32 {}
+
+fn foo<T: Foo>()
+where
+    [(); T::ASSOC]: ,
+{
+    let _iced: Iced<T> = return;
+}
+
+fn main() {
+    foo::<u32>();
+}
diff --git a/src/test/ui/const-generics/conservative_is_privately_uninhabited_uses_correct_param_env-2.rs b/src/test/ui/const-generics/conservative_is_privately_uninhabited_uses_correct_param_env-2.rs
new file mode 100644 (file)
index 0000000..96dbac1
--- /dev/null
@@ -0,0 +1,20 @@
+// run-pass
+#![feature(const_generics, const_evaluatable_checked)]
+#![allow(incomplete_features)]
+
+// This tests that the `conservative_is_privately_uninhabited` fn doesn't cause
+// ICEs by trying to evaluate `T::ASSOC` with an incorrect `ParamEnv`.
+
+trait Foo {
+    const ASSOC: usize = 1;
+}
+
+struct Iced<T: Foo>(T, [(); T::ASSOC])
+where
+    [(); T::ASSOC]: ;
+
+impl Foo for u32 {}
+
+fn main() {
+    let _iced: Iced<u32> = return;
+}
diff --git a/src/test/ui/consts/const-as-fn.rs b/src/test/ui/consts/const-as-fn.rs
new file mode 100644 (file)
index 0000000..388f907
--- /dev/null
@@ -0,0 +1,5 @@
+const FOO: usize = 0;
+
+fn main() {
+    FOO(); //~ ERROR expected function, found `usize`
+}
diff --git a/src/test/ui/consts/const-as-fn.stderr b/src/test/ui/consts/const-as-fn.stderr
new file mode 100644 (file)
index 0000000..b8dd413
--- /dev/null
@@ -0,0 +1,14 @@
+error[E0618]: expected function, found `usize`
+  --> $DIR/const-as-fn.rs:4:5
+   |
+LL | const FOO: usize = 0;
+   | --------------------- `FOO` defined here
+...
+LL |     FOO();
+   |     ^^^--
+   |     |
+   |     call expression requires function
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0618`.
index 6ddda3bf8b528c1dcb0725d4ffa98378d54525db..714c8d1e4d75ce0182cbcbaa0fa3bbe254b4054a 100644 (file)
@@ -18,7 +18,7 @@ error[E0618]: expected function, found `i32`
   --> $DIR/E0618.rs:9:5
    |
 LL |     let x = 0i32;
-   |         - `i32` defined here
+   |         - `x` has type `i32`
 LL |     x();
    |     ^--
    |     |
index 0dc09704e7fab490235549bbb0fcef1a256b13df..91fc398f7418396ea23c9f65f07cb039cba696d5 100644 (file)
@@ -8,7 +8,11 @@ pub trait IpuIterator {
     fn ipu_flatten(&self) -> u32 {
         0
     }
+    #[unstable(feature = "assoc_const_ipu_iter", issue = "99999")]
+    const C: i32;
 }
 
 #[stable(feature = "ipu_iterator", since = "1.0.0")]
-impl IpuIterator for char {}
+impl IpuIterator for char {
+    const C: i32 = 42;
+}
index 964f35ddadec57c6271ea126965e4230a20c6e59..e00adda5c33820bbafd817097bc95c9610ad6343 100644 (file)
@@ -2,6 +2,10 @@ pub trait IpuItertools {
     fn ipu_flatten(&self) -> u32 {
         1
     }
+
+    const C: i32;
 }
 
-impl IpuItertools for char {}
+impl IpuItertools for char {
+    const C: i32 = 1;
+}
index 0b957700182415b948028d3a9d243742ffc0bbb9..86bb62b8a5f64ad3b449b02349763bb2303bfb80 100644 (file)
@@ -14,6 +14,9 @@
 
 fn main() {
     assert_eq!('x'.ipu_flatten(), 1);
-    //~^ WARN a method with this name may be added to the standard library in the future
-    //~^^ WARN once this method is added to the standard library, the ambiguity may cause an error
+//~^ WARN an associated function with this name may be added to the standard library in the future
+//~| WARN once this associated item is added to the standard library, the ambiguity may cause an
+    assert_eq!(char::C, 1);
+//~^ WARN an associated constant with this name may be added to the standard library in the future
+//~| WARN once this associated item is added to the standard library, the ambiguity may cause an
 }
index df52012463255ba34c8b7779b5255eae5d43f5bb..2c282e610b29c6d426e5ca5644521aa47ec97cbb 100644 (file)
@@ -1,14 +1,24 @@
-warning: a method with this name may be added to the standard library in the future
+warning: an associated function with this name may be added to the standard library in the future
   --> $DIR/inference_unstable.rs:16:20
    |
 LL |     assert_eq!('x'.ipu_flatten(), 1);
    |                    ^^^^^^^^^^^
    |
    = note: `#[warn(unstable_name_collisions)]` on by default
-   = warning: once this method is added to the standard library, the ambiguity may cause an error or change in behavior!
+   = warning: once this associated item is added to the standard library, the ambiguity may cause an error or change in behavior!
    = note: for more information, see issue #48919 <https://github.com/rust-lang/rust/issues/48919>
    = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_flatten(...)` to keep using the current method
    = help: add `#![feature(ipu_flatten)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_flatten`
 
-warning: 1 warning emitted
+warning: an associated constant with this name may be added to the standard library in the future
+  --> $DIR/inference_unstable.rs:19:16
+   |
+LL |     assert_eq!(char::C, 1);
+   |                ^^^^^^^ help: use the fully qualified path to the associated const: `<char as IpuItertools>::C`
+   |
+   = warning: once this associated item is added to the standard library, the ambiguity may cause an error or change in behavior!
+   = note: for more information, see issue #48919 <https://github.com/rust-lang/rust/issues/48919>
+   = help: add `#![feature(assoc_const_ipu_iter)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::C`
+
+warning: 2 warnings emitted
 
diff --git a/src/test/ui/invalid-rustc_args_required_const-arguments.rs b/src/test/ui/invalid-rustc_args_required_const-arguments.rs
deleted file mode 100644 (file)
index 99508ba..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#![feature(rustc_attrs)]
-
-#[rustc_args_required_const(0)] //~ ERROR index exceeds number of arguments
-fn foo1() {}
-
-#[rustc_args_required_const(1)] //~ ERROR index exceeds number of arguments
-fn foo2(_: u8) {}
-
-#[rustc_args_required_const(a)] //~ ERROR arguments should be non-negative integers
-fn foo4() {}
-
-#[rustc_args_required_const(1, a, 2, b)] //~ ERROR arguments should be non-negative integers
-fn foo5(_: u8, _: u8, _: u8) {}
-
-#[rustc_args_required_const(0)] //~ ERROR attribute should be applied to a function
-struct S;
-
-#[rustc_args_required_const(0usize)] //~ ERROR suffixed literals are not allowed in attributes
-fn foo6(_: u8) {}
-
-extern {
-    #[rustc_args_required_const(1)] //~ ERROR index exceeds number of arguments
-    fn foo7(_: u8);
-}
-
-#[rustc_args_required_const] //~ ERROR malformed `rustc_args_required_const` attribute
-fn bar1() {}
-
-#[rustc_args_required_const = 1] //~ ERROR malformed `rustc_args_required_const` attribute
-fn bar2() {}
-
-fn main() {}
diff --git a/src/test/ui/invalid-rustc_args_required_const-arguments.stderr b/src/test/ui/invalid-rustc_args_required_const-arguments.stderr
deleted file mode 100644 (file)
index 932344f..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-error: suffixed literals are not allowed in attributes
-  --> $DIR/invalid-rustc_args_required_const-arguments.rs:18:29
-   |
-LL | #[rustc_args_required_const(0usize)]
-   |                             ^^^^^^
-   |
-   = help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
-
-error: malformed `rustc_args_required_const` attribute input
-  --> $DIR/invalid-rustc_args_required_const-arguments.rs:26:1
-   |
-LL | #[rustc_args_required_const]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_args_required_const(N)]`
-
-error: malformed `rustc_args_required_const` attribute input
-  --> $DIR/invalid-rustc_args_required_const-arguments.rs:29:1
-   |
-LL | #[rustc_args_required_const = 1]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_args_required_const(N)]`
-
-error: index exceeds number of arguments
-  --> $DIR/invalid-rustc_args_required_const-arguments.rs:3:29
-   |
-LL | #[rustc_args_required_const(0)]
-   |                             ^ there are only 0 arguments
-
-error: index exceeds number of arguments
-  --> $DIR/invalid-rustc_args_required_const-arguments.rs:6:29
-   |
-LL | #[rustc_args_required_const(1)]
-   |                             ^ there is only 1 argument
-
-error: arguments should be non-negative integers
-  --> $DIR/invalid-rustc_args_required_const-arguments.rs:9:29
-   |
-LL | #[rustc_args_required_const(a)]
-   |                             ^
-
-error: arguments should be non-negative integers
-  --> $DIR/invalid-rustc_args_required_const-arguments.rs:12:32
-   |
-LL | #[rustc_args_required_const(1, a, 2, b)]
-   |                                ^     ^
-
-error: attribute should be applied to a function
-  --> $DIR/invalid-rustc_args_required_const-arguments.rs:15:1
-   |
-LL | #[rustc_args_required_const(0)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-LL | struct S;
-   | --------- not a function
-
-error: index exceeds number of arguments
-  --> $DIR/invalid-rustc_args_required_const-arguments.rs:22:33
-   |
-LL |     #[rustc_args_required_const(1)]
-   |                                 ^ there is only 1 argument
-
-error: aborting due to 9 previous errors
-
diff --git a/src/test/ui/invalid/invalid-rustc_args_required_const-arguments.rs b/src/test/ui/invalid/invalid-rustc_args_required_const-arguments.rs
new file mode 100644 (file)
index 0000000..99508ba
--- /dev/null
@@ -0,0 +1,32 @@
+#![feature(rustc_attrs)]
+
+#[rustc_args_required_const(0)] //~ ERROR index exceeds number of arguments
+fn foo1() {}
+
+#[rustc_args_required_const(1)] //~ ERROR index exceeds number of arguments
+fn foo2(_: u8) {}
+
+#[rustc_args_required_const(a)] //~ ERROR arguments should be non-negative integers
+fn foo4() {}
+
+#[rustc_args_required_const(1, a, 2, b)] //~ ERROR arguments should be non-negative integers
+fn foo5(_: u8, _: u8, _: u8) {}
+
+#[rustc_args_required_const(0)] //~ ERROR attribute should be applied to a function
+struct S;
+
+#[rustc_args_required_const(0usize)] //~ ERROR suffixed literals are not allowed in attributes
+fn foo6(_: u8) {}
+
+extern {
+    #[rustc_args_required_const(1)] //~ ERROR index exceeds number of arguments
+    fn foo7(_: u8);
+}
+
+#[rustc_args_required_const] //~ ERROR malformed `rustc_args_required_const` attribute
+fn bar1() {}
+
+#[rustc_args_required_const = 1] //~ ERROR malformed `rustc_args_required_const` attribute
+fn bar2() {}
+
+fn main() {}
diff --git a/src/test/ui/invalid/invalid-rustc_args_required_const-arguments.stderr b/src/test/ui/invalid/invalid-rustc_args_required_const-arguments.stderr
new file mode 100644 (file)
index 0000000..932344f
--- /dev/null
@@ -0,0 +1,60 @@
+error: suffixed literals are not allowed in attributes
+  --> $DIR/invalid-rustc_args_required_const-arguments.rs:18:29
+   |
+LL | #[rustc_args_required_const(0usize)]
+   |                             ^^^^^^
+   |
+   = help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
+
+error: malformed `rustc_args_required_const` attribute input
+  --> $DIR/invalid-rustc_args_required_const-arguments.rs:26:1
+   |
+LL | #[rustc_args_required_const]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_args_required_const(N)]`
+
+error: malformed `rustc_args_required_const` attribute input
+  --> $DIR/invalid-rustc_args_required_const-arguments.rs:29:1
+   |
+LL | #[rustc_args_required_const = 1]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_args_required_const(N)]`
+
+error: index exceeds number of arguments
+  --> $DIR/invalid-rustc_args_required_const-arguments.rs:3:29
+   |
+LL | #[rustc_args_required_const(0)]
+   |                             ^ there are only 0 arguments
+
+error: index exceeds number of arguments
+  --> $DIR/invalid-rustc_args_required_const-arguments.rs:6:29
+   |
+LL | #[rustc_args_required_const(1)]
+   |                             ^ there is only 1 argument
+
+error: arguments should be non-negative integers
+  --> $DIR/invalid-rustc_args_required_const-arguments.rs:9:29
+   |
+LL | #[rustc_args_required_const(a)]
+   |                             ^
+
+error: arguments should be non-negative integers
+  --> $DIR/invalid-rustc_args_required_const-arguments.rs:12:32
+   |
+LL | #[rustc_args_required_const(1, a, 2, b)]
+   |                                ^     ^
+
+error: attribute should be applied to a function
+  --> $DIR/invalid-rustc_args_required_const-arguments.rs:15:1
+   |
+LL | #[rustc_args_required_const(0)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | struct S;
+   | --------- not a function
+
+error: index exceeds number of arguments
+  --> $DIR/invalid-rustc_args_required_const-arguments.rs:22:33
+   |
+LL |     #[rustc_args_required_const(1)]
+   |                                 ^ there is only 1 argument
+
+error: aborting due to 9 previous errors
+
diff --git a/src/test/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs b/src/test/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs
new file mode 100644 (file)
index 0000000..3d8478f
--- /dev/null
@@ -0,0 +1,38 @@
+#![feature(rustc_attrs)]
+
+#[rustc_legacy_const_generics(0)] //~ ERROR #[rustc_legacy_const_generics] must have one index for
+fn foo1() {}
+
+#[rustc_legacy_const_generics(1)] //~ ERROR index exceeds number of arguments
+fn foo2<const X: usize>() {}
+
+#[rustc_legacy_const_generics(2)] //~ ERROR index exceeds number of arguments
+fn foo3<const X: usize>(_: u8) {}
+
+#[rustc_legacy_const_generics(a)] //~ ERROR arguments should be non-negative integers
+fn foo4<const X: usize>() {}
+
+#[rustc_legacy_const_generics(1, a, 2, b)] //~ ERROR arguments should be non-negative integers
+fn foo5<const X: usize, const Y: usize, const Z: usize, const W: usize>() {}
+
+#[rustc_legacy_const_generics(0)] //~ ERROR attribute should be applied to a function
+struct S;
+
+#[rustc_legacy_const_generics(0usize)] //~ ERROR suffixed literals are not allowed in attributes
+fn foo6<const X: usize>() {}
+
+extern {
+    #[rustc_legacy_const_generics(1)] //~ ERROR attribute should be applied to a function
+    fn foo7<const X: usize>(); //~ ERROR foreign items may not have const parameters
+}
+
+#[rustc_legacy_const_generics(0)] //~ ERROR #[rustc_legacy_const_generics] functions must only have
+fn foo8<X>() {}
+
+#[rustc_legacy_const_generics] //~ ERROR malformed `rustc_legacy_const_generics` attribute
+fn bar1() {}
+
+#[rustc_legacy_const_generics = 1] //~ ERROR malformed `rustc_legacy_const_generics` attribute
+fn bar2() {}
+
+fn main() {}
diff --git a/src/test/ui/invalid/invalid-rustc_legacy_const_generics-arguments.stderr b/src/test/ui/invalid/invalid-rustc_legacy_const_generics-arguments.stderr
new file mode 100644 (file)
index 0000000..1f55a8e
--- /dev/null
@@ -0,0 +1,87 @@
+error: suffixed literals are not allowed in attributes
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:21:31
+   |
+LL | #[rustc_legacy_const_generics(0usize)]
+   |                               ^^^^^^
+   |
+   = help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
+
+error: malformed `rustc_legacy_const_generics` attribute input
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:32:1
+   |
+LL | #[rustc_legacy_const_generics]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_legacy_const_generics(N)]`
+
+error: malformed `rustc_legacy_const_generics` attribute input
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:35:1
+   |
+LL | #[rustc_legacy_const_generics = 1]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_legacy_const_generics(N)]`
+
+error: #[rustc_legacy_const_generics] must have one index for each generic parameter
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:3:1
+   |
+LL | #[rustc_legacy_const_generics(0)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn foo1() {}
+   |        - generic parameters
+
+error: index exceeds number of arguments
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:6:31
+   |
+LL | #[rustc_legacy_const_generics(1)]
+   |                               ^ there is only 1 argument
+
+error: index exceeds number of arguments
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:9:31
+   |
+LL | #[rustc_legacy_const_generics(2)]
+   |                               ^ there are only 2 arguments
+
+error: arguments should be non-negative integers
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:12:31
+   |
+LL | #[rustc_legacy_const_generics(a)]
+   |                               ^
+
+error: arguments should be non-negative integers
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:15:34
+   |
+LL | #[rustc_legacy_const_generics(1, a, 2, b)]
+   |                                  ^     ^
+
+error: attribute should be applied to a function
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:18:1
+   |
+LL | #[rustc_legacy_const_generics(0)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | struct S;
+   | --------- not a function
+
+error: #[rustc_legacy_const_generics] functions must only have const generics
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:29:1
+   |
+LL | #[rustc_legacy_const_generics(0)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn foo8<X>() {}
+   |         - non-const generic parameter
+
+error: attribute should be applied to a function
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:25:5
+   |
+LL |     #[rustc_legacy_const_generics(1)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     fn foo7<const X: usize>();
+   |     -------------------------- not a function
+
+error[E0044]: foreign items may not have const parameters
+  --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:26:5
+   |
+LL |     fn foo7<const X: usize>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ can't have const parameters
+   |
+   = help: replace the const parameters with concrete consts
+
+error: aborting due to 12 previous errors
+
+For more information about this error, try `rustc --explain E0044`.
index 9e28a665e30fb1bce9c5a95534106a1aaff99d3e..f64b61aaeb05c942f3f5c6678e5d844c2d80ff38 100644 (file)
@@ -2,7 +2,7 @@ error[E0618]: expected function, found `i32`
   --> $DIR/issue-10969.rs:2:5
    |
 LL | fn func(i: i32) {
-   |         - `i32` defined here
+   |         - `i` has type `i32`
 LL |     i();
    |     ^--
    |     |
@@ -12,7 +12,7 @@ error[E0618]: expected function, found `i32`
   --> $DIR/issue-10969.rs:6:5
    |
 LL |     let i = 0i32;
-   |         - `i32` defined here
+   |         - `i` has type `i32`
 LL |     i();
    |     ^--
    |     |
index 77bb3802d8603d9498e3c33ac5900188209a323f..0405ce551b0ab5db73097001fb0f075851e3c8a1 100644 (file)
@@ -2,7 +2,7 @@ error[E0618]: expected function, found `U`
   --> $DIR/issue-21701.rs:2:13
    |
 LL | fn foo<U>(t: U) {
-   |           - `U` defined here
+   |           - `t` has type `U`
 LL |     let y = t();
    |             ^--
    |             |
index 8d8601b311166e2e5e189afeac0416edf747a50d..3fff91acbc25fa3c0ec3008653543a47c083e75a 100644 (file)
@@ -2,7 +2,7 @@ error[E0618]: expected function, found `&str`
   --> $DIR/issue-22468.rs:3:13
    |
 LL |     let foo = "bar";
-   |         --- `&str` defined here
+   |         --- `foo` has type `&str`
 LL |     let x = foo("baz");
    |             ^^^-------
    |             |
index f58c1bfe644fc1cce6512b5ebfa401aedd025ca1..91d28a5e1e1eff70dd6b4a5a8bae5acfc7fa7ff0 100644 (file)
@@ -5,7 +5,7 @@ LL |         $not_a_function($some_argument)
    |         ------------------------------- call expression requires function
 ...
 LL |     let mut value_a = 0;
-   |         ----------- `{integer}` defined here
+   |         ----------- `value_a` has type `{integer}`
 LL |     let mut value_b = 0;
 LL |     macro_panic!(value_a, value_b);
    |                  ^^^^^^^
diff --git a/src/test/ui/legacy-const-generics-bad.rs b/src/test/ui/legacy-const-generics-bad.rs
new file mode 100644 (file)
index 0000000..538eee3
--- /dev/null
@@ -0,0 +1,16 @@
+// aux-build:legacy-const-generics.rs
+
+extern crate legacy_const_generics;
+
+fn foo<const N: usize>() {
+    let a = 1;
+    legacy_const_generics::foo(0, a, 2);
+    //~^ ERROR attempt to use a non-constant value in a constant
+
+    legacy_const_generics::foo(0, N, 2);
+
+    legacy_const_generics::foo(0, N + 1, 2);
+    //~^ ERROR generic parameters may not be used in const operations
+}
+
+fn main() {}
diff --git a/src/test/ui/legacy-const-generics-bad.stderr b/src/test/ui/legacy-const-generics-bad.stderr
new file mode 100644 (file)
index 0000000..5a44b8e
--- /dev/null
@@ -0,0 +1,20 @@
+error[E0435]: attempt to use a non-constant value in a constant
+  --> $DIR/legacy-const-generics-bad.rs:7:35
+   |
+LL |     let a = 1;
+   |     ----- help: consider using `const` instead of `let`: `const a`
+LL |     legacy_const_generics::foo(0, a, 2);
+   |                                   ^ non-constant value
+
+error: generic parameters may not be used in const operations
+  --> $DIR/legacy-const-generics-bad.rs:12:35
+   |
+LL |     legacy_const_generics::foo(0, N + 1, 2);
+   |                                   ^ cannot perform const operation using `N`
+   |
+   = help: const parameters may only be used as standalone arguments, i.e. `N`
+   = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0435`.
diff --git a/src/test/ui/legacy-const-generics.rs b/src/test/ui/legacy-const-generics.rs
new file mode 100644 (file)
index 0000000..9abc72d
--- /dev/null
@@ -0,0 +1,18 @@
+// aux-build:legacy-const-generics.rs
+// run-pass
+
+#![feature(rustc_attrs)]
+
+extern crate legacy_const_generics;
+
+#[rustc_legacy_const_generics(1)]
+pub fn bar<const Y: usize>(x: usize, z: usize) -> [usize; 3] {
+    [x, Y, z]
+}
+
+fn main() {
+    assert_eq!(legacy_const_generics::foo(0 + 0, 1 + 1, 2 + 2), [0, 2, 4]);
+    assert_eq!(legacy_const_generics::foo::<{1 + 1}>(0 + 0, 2 + 2), [0, 2, 4]);
+    // FIXME: Only works cross-crate
+    //assert_eq!(bar(0, 1, 2), [0, 1, 2]);
+}
index c54baf00b27f2c8ef395bd044f30e65f5a90ef67..691df91268b246212e0e4a360e6ecf076e5a4eb6 100644 (file)
@@ -14,7 +14,7 @@ error[E0618]: expected function, found `{integer}`
   --> $DIR/parse-error-correct.rs:7:13
    |
 LL |     let y = 42;
-   |         - `{integer}` defined here
+   |         - `y` has type `{integer}`
 LL |     let x = y.;
 LL |     let x = y.();
    |             ^---
diff --git a/src/test/ui/structs/80853.rs b/src/test/ui/structs/80853.rs
new file mode 100644 (file)
index 0000000..242d0af
--- /dev/null
@@ -0,0 +1,7 @@
+struct S;
+
+fn repro_ref(thing: S) {
+    thing(); //~ ERROR expected function, found `S`
+}
+
+fn main() {}
diff --git a/src/test/ui/structs/80853.stderr b/src/test/ui/structs/80853.stderr
new file mode 100644 (file)
index 0000000..8a38e32
--- /dev/null
@@ -0,0 +1,13 @@
+error[E0618]: expected function, found `S`
+  --> $DIR/80853.rs:4:5
+   |
+LL | fn repro_ref(thing: S) {
+   |              ----- `thing` has type `S`
+LL |     thing();
+   |     ^^^^^--
+   |     |
+   |     call expression requires function
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0618`.
diff --git a/src/test/ui/suggestions/do-not-attempt-to-add-suggestions-with-no-changes.rs b/src/test/ui/suggestions/do-not-attempt-to-add-suggestions-with-no-changes.rs
new file mode 100644 (file)
index 0000000..a25be86
--- /dev/null
@@ -0,0 +1,5 @@
+use std::result;
+impl result { //~ ERROR expected type, found module `result`
+    fn into_future() -> Err {} //~ ERROR expected type, found variant `Err`
+}
+fn main() {}
diff --git a/src/test/ui/suggestions/do-not-attempt-to-add-suggestions-with-no-changes.stderr b/src/test/ui/suggestions/do-not-attempt-to-add-suggestions-with-no-changes.stderr
new file mode 100644 (file)
index 0000000..59ccb29
--- /dev/null
@@ -0,0 +1,20 @@
+error[E0573]: expected type, found module `result`
+  --> $DIR/do-not-attempt-to-add-suggestions-with-no-changes.rs:2:6
+   |
+LL | impl result {
+   |      ^^^^^^ help: an enum with a similar name exists: `Result`
+   | 
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+LL | pub enum Result<T, E> {
+   | --------------------- similarly named enum `Result` defined here
+
+error[E0573]: expected type, found variant `Err`
+  --> $DIR/do-not-attempt-to-add-suggestions-with-no-changes.rs:3:25
+   |
+LL |     fn into_future() -> Err {}
+   |                         ^^^ not a type
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0573`.
diff --git a/src/test/ui/suggestions/issue-82361.fixed b/src/test/ui/suggestions/issue-82361.fixed
new file mode 100644 (file)
index 0000000..d72de98
--- /dev/null
@@ -0,0 +1,24 @@
+// run-rustfix
+
+fn main() {
+    let a: usize = 123;
+    let b: &usize = &a;
+
+    if true {
+        a
+    } else {
+        *b //~ ERROR `if` and `else` have incompatible types [E0308]
+    };
+
+    if true {
+        1
+    } else {
+        1 //~ ERROR `if` and `else` have incompatible types [E0308]
+    };
+
+    if true {
+        1
+    } else {
+        1 //~ ERROR `if` and `else` have incompatible types [E0308]
+    };
+}
diff --git a/src/test/ui/suggestions/issue-82361.rs b/src/test/ui/suggestions/issue-82361.rs
new file mode 100644 (file)
index 0000000..c068f6d
--- /dev/null
@@ -0,0 +1,24 @@
+// run-rustfix
+
+fn main() {
+    let a: usize = 123;
+    let b: &usize = &a;
+
+    if true {
+        a
+    } else {
+        b //~ ERROR `if` and `else` have incompatible types [E0308]
+    };
+
+    if true {
+        1
+    } else {
+        &1 //~ ERROR `if` and `else` have incompatible types [E0308]
+    };
+
+    if true {
+        1
+    } else {
+        &mut 1 //~ ERROR `if` and `else` have incompatible types [E0308]
+    };
+}
diff --git a/src/test/ui/suggestions/issue-82361.stderr b/src/test/ui/suggestions/issue-82361.stderr
new file mode 100644 (file)
index 0000000..c19d59c
--- /dev/null
@@ -0,0 +1,48 @@
+error[E0308]: `if` and `else` have incompatible types
+  --> $DIR/issue-82361.rs:10:9
+   |
+LL | /     if true {
+LL | |         a
+   | |         - expected because of this
+LL | |     } else {
+LL | |         b
+   | |         ^
+   | |         |
+   | |         expected `usize`, found `&usize`
+   | |         help: consider dereferencing the borrow: `*b`
+LL | |     };
+   | |_____- `if` and `else` have incompatible types
+
+error[E0308]: `if` and `else` have incompatible types
+  --> $DIR/issue-82361.rs:16:9
+   |
+LL | /     if true {
+LL | |         1
+   | |         - expected because of this
+LL | |     } else {
+LL | |         &1
+   | |         -^
+   | |         |
+   | |         expected integer, found `&{integer}`
+   | |         help: consider removing the `&`
+LL | |     };
+   | |_____- `if` and `else` have incompatible types
+
+error[E0308]: `if` and `else` have incompatible types
+  --> $DIR/issue-82361.rs:22:9
+   |
+LL | /     if true {
+LL | |         1
+   | |         - expected because of this
+LL | |     } else {
+LL | |         &mut 1
+   | |         -----^
+   | |         |
+   | |         expected integer, found `&mut {integer}`
+   | |         help: consider removing the `&mut`
+LL | |     };
+   | |_____- `if` and `else` have incompatible types
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
index 67846336acaaf27d337cdea040c668e1216a3216..d4d44049c9186d15e96a24f0063aef0e11a667a6 100644 (file)
@@ -4,6 +4,7 @@
 // error-pattern: [trigger_delay_span_bug] trigger a delay span bug
 // normalize-stderr-test "note: .*\n\n" -> ""
 // normalize-stderr-test "thread 'rustc' panicked.*\n" -> ""
+// rustc-env:RUST_BACKTRACE=0
 
 #![feature(rustc_attrs)]
 
index ed65b69ebcab1a6ad0adcdf6fdce0cec78ee6dbd..c23c2b81b9767904dec24a0ddbc5c7101c31cb45 100644 (file)
@@ -1,5 +1,5 @@
 error: internal compiler error: delayed span bug triggered by #[rustc_error(delay_span_bug_from_inside_query)]
-  --> $DIR/delay_span_bug.rs:11:1
+  --> $DIR/delay_span_bug.rs:12:1
    |
 LL | fn main() {}
    | ^^^^^^^^^
index 5442d8585941b7f4f9fbd33019bbaa1135ff8f4c..de3e9ed6cf910e5cda2d8c97bf10b83da0c0993e 100644 (file)
@@ -4,6 +4,7 @@
 // error-pattern: [eval_to_allocation_raw] const-evaluating + checking `C`
 // normalize-stderr-test "note: .*\n\n" -> ""
 // normalize-stderr-test "thread 'rustc' panicked.*\n" -> ""
+// rustc-env:RUST_BACKTRACE=0
 
 #![crate_type = "rlib"]
 
index 61eb85c40a112c549c55f81e9437f7e64e503063..8f67571c2990ea56a1c2494fd2331fb36cb5423f 100644 (file)
@@ -1,5 +1,5 @@
 error[E0080]: could not evaluate static initializer
-  --> $DIR/err.rs:10:21
+  --> $DIR/err.rs:11:21
    |
 LL | pub static C: u32 = 0 - 1;
    |                     ^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow
diff --git a/src/test/ui/typeck/auxiliary/issue-81943-lib.rs b/src/test/ui/typeck/auxiliary/issue-81943-lib.rs
new file mode 100644 (file)
index 0000000..521c54f
--- /dev/null
@@ -0,0 +1,7 @@
+pub fn g(t: i32) -> i32 { t }
+// This function imitates `dbg!` so that future changes
+// to its macro definition won't make this test a dud.
+#[macro_export]
+macro_rules! d {
+  ($e:expr) => { match $e { x => { $crate::g(x) } } }
+}
diff --git a/src/test/ui/typeck/issue-81943.rs b/src/test/ui/typeck/issue-81943.rs
new file mode 100644 (file)
index 0000000..18f5970
--- /dev/null
@@ -0,0 +1,13 @@
+// aux-build:issue-81943-lib.rs
+extern crate issue_81943_lib as lib;
+
+fn f<F: Fn(i32)>(f: F) { f(0); }
+fn g(t: i32) -> i32 { t }
+fn main() {
+  f(|x| lib::d!(x)); //~ERROR
+  f(|x| match x { tmp => { g(tmp) } }); //~ERROR
+  macro_rules! d {
+    ($e:expr) => { match $e { x => { g(x) } } } //~ERROR
+  }
+  f(|x| d!(x));
+}
diff --git a/src/test/ui/typeck/issue-81943.stderr b/src/test/ui/typeck/issue-81943.stderr
new file mode 100644 (file)
index 0000000..a30facf
--- /dev/null
@@ -0,0 +1,51 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-81943.rs:7:9
+   |
+LL |   f(|x| lib::d!(x));
+   |         ^^^^^^^^^^ expected `()`, found `i32`
+   |
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0308]: mismatched types
+  --> $DIR/issue-81943.rs:8:28
+   |
+LL |   f(|x| match x { tmp => { g(tmp) } });
+   |         -------------------^^^^^^----
+   |         |                  |
+   |         |                  expected `()`, found `i32`
+   |         expected this to be `()`
+   |
+help: consider using a semicolon here
+   |
+LL |   f(|x| match x { tmp => { g(tmp); } });
+   |                                  ^
+help: consider using a semicolon here
+   |
+LL |   f(|x| match x { tmp => { g(tmp) } };);
+   |                                      ^
+
+error[E0308]: mismatched types
+  --> $DIR/issue-81943.rs:10:38
+   |
+LL |     ($e:expr) => { match $e { x => { g(x) } } }
+   |                    ------------------^^^^----
+   |                    |                 |
+   |                    |                 expected `()`, found `i32`
+   |                    expected this to be `()`
+LL |   }
+LL |   f(|x| d!(x));
+   |         ----- in this macro invocation
+   |
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider using a semicolon here
+   |
+LL |     ($e:expr) => { match $e { x => { g(x); } } }
+   |                                          ^
+help: consider using a semicolon here
+   |
+LL |     ($e:expr) => { match $e { x => { g(x) } }; }
+   |                                              ^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
index d8f0c44148cae9d64ffe41a24f8b840126a8a6dd..2bc87db123d4d38d126ba80a597f43b0d3e8524b 100644 (file)
@@ -1,7 +1,7 @@
 ---
 name: Bug Report
 about: Create a bug report for Clippy
-labels: L-bug
+labels: C-bug
 ---
 <!--
 Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
index f46828fec91b25594d06952716e4357053ab4df4..53341c7a928afd77a9ccac06bb3f4b9b992e31c2 100644 (file)
@@ -1,7 +1,7 @@
 ---
 name: Bug Report (False Negative)
 about: Create a bug report about missing warnings from a lint
-labels: L-bug, L-false-negative
+labels: C-bug, I-false-negative
 ---
 <!--
 Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
index 92a7373fc27d51f372cd389d1952800635400535..34fd6f4bdb7105f86b548c51fc864a54b12c79fa 100644 (file)
@@ -1,7 +1,7 @@
 ---
 name: Bug Report (False Positive)
 about: Create a bug report about a wrongly emitted lint warning
-labels: L-bug, L-false-positive
+labels: C-bug, I-false-positive
 ---
 <!--
 Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
index 3abe76bf2c49723fee4bdd125861ce3a461c1d93..0b7cd1ed0fb60fb7921be8e6393a04de1e4f80cb 100644 (file)
@@ -1,7 +1,7 @@
 ---
 name: Internal Compiler Error
 about: Create a report for an internal compiler error in Clippy.
-labels: L-bug, L-crash
+labels: C-bug, I-ICE
 ---
 <!--
 Thank you for finding an Internal Compiler Error! 🧊  If possible, try to provide
index 98fd0df685fdb31711ea1c8234f9afea43e62c0a..e182c99ce06a24318c16a81ea6de1c612f0769f1 100644 (file)
@@ -1,7 +1,7 @@
 ---
 name: New lint suggestion
 about: Suggest a new Clippy lint.
-labels: L-lint
+labels: A-lint
 ---
 
 ### What it does
index 9d5e12aac5f7b73701396d4703666d75cd4e12e8..f6ac936df4d6b848207e47bfba419bfb1f3778fb 100644 (file)
@@ -53,16 +53,8 @@ jobs:
     - name: Test "--fix -Zunstable-options"
       run: cargo run --features deny-warnings,internal-lints --bin cargo-clippy -- clippy --fix -Zunstable-options
 
-    - name: Test
-      run: cargo test --features deny-warnings,internal-lints
-
-    - name: Test clippy_lints
-      run: cargo test --features deny-warnings,internal-lints
-      working-directory: clippy_lints
-
-    - name: Test rustc_tools_util
-      run: cargo test --features deny-warnings
-      working-directory: rustc_tools_util
+    - name: Test Workspace
+      run: cargo test --all --features deny-warnings,internal-lints
 
     - name: Test clippy_dev
       run: cargo test --features deny-warnings
index 5d846eb64c78e5c0f78d72e682a961605879f928..9d24b0293c4a73d156dcc0e7cb2465b20417d460 100644 (file)
@@ -112,16 +112,8 @@ jobs:
     - name: Build
       run: cargo build --features deny-warnings,internal-lints
 
-    - name: Test
-      run: cargo test --features deny-warnings,internal-lints
-
-    - name: Test clippy_lints
-      run: cargo test --features deny-warnings,internal-lints
-      working-directory: clippy_lints
-
-    - name: Test rustc_tools_util
-      run: cargo test --features deny-warnings
-      working-directory: rustc_tools_util
+    - name: Test Workspace
+      run: cargo test --all --features deny-warnings,internal-lints
 
     - name: Test clippy_dev
       run: cargo test --features deny-warnings
index adf5e8feddf4c507efc627259e53cb146bdb45ea..139129d55e330af9d2f323986a1c621878152625 100644 (file)
@@ -18,6 +18,7 @@ out
 *Cargo.lock
 /target
 /clippy_lints/target
+/clippy_utils/target
 /clippy_workspace_tests/target
 /clippy_dev/target
 /rustc_tools_util/target
index 7c79fe88816f13176974589209860dac1cbf3a8f..d96c74b6e412650ad73369573a01abd6549fcce5 100644 (file)
@@ -6,13 +6,128 @@ document.
 
 ## Unreleased / In Rust Nightly
 
-[4911ab1...master](https://github.com/rust-lang/rust-clippy/compare/4911ab1...master)
+[3e41797...master](https://github.com/rust-lang/rust-clippy/compare/3e41797...master)
+
+## Rust 1.51
+
+Current beta, release 2021-03-25
+
+[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
+
+### New Lints
+
+* [`upper_case_acronyms`]
+  [#6475](https://github.com/rust-lang/rust-clippy/pull/6475)
+* [`from_over_into`] [#6476](https://github.com/rust-lang/rust-clippy/pull/6476)
+* [`case_sensitive_file_extension_comparisons`]
+  [#6500](https://github.com/rust-lang/rust-clippy/pull/6500)
+* [`needless_question_mark`]
+  [#6507](https://github.com/rust-lang/rust-clippy/pull/6507)
+* [`missing_panics_doc`]
+  [#6523](https://github.com/rust-lang/rust-clippy/pull/6523)
+* [`redundant_slicing`]
+  [#6528](https://github.com/rust-lang/rust-clippy/pull/6528)
+* [`vec_init_then_push`]
+  [#6538](https://github.com/rust-lang/rust-clippy/pull/6538)
+* [`ptr_as_ptr`] [#6542](https://github.com/rust-lang/rust-clippy/pull/6542)
+* [`collapsible_else_if`] (split out from `collapsible_if`)
+  [#6544](https://github.com/rust-lang/rust-clippy/pull/6544)
+* [`inspect_for_each`] [#6577](https://github.com/rust-lang/rust-clippy/pull/6577)
+* [`manual_filter_map`]
+  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
+* [`exhaustive_enums`]
+  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
+* [`exhaustive_structs`]
+  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
+
+### Moves and Deprecations
+
+* Replace [`find_map`] with [`manual_find_map`]
+  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
+* [`unknown_clippy_lints`] Now integrated in the `unknown_lints` rustc lint
+  [#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
+
+### Enhancements
+
+* [`ptr_arg`] Now also suggests to use `&Path` instead of `&PathBuf`
+  [#6506](https://github.com/rust-lang/rust-clippy/pull/6506)
+* [`cast_ptr_alignment`] Also lint when the `pointer::cast` method is used
+  [#6557](https://github.com/rust-lang/rust-clippy/pull/6557)
+* [`collapsible_match`] Now also deals with `&` and `*` operators in the `match`
+  scrutinee [#6619](https://github.com/rust-lang/rust-clippy/pull/6619)
+
+### False Positive Fixes
+
+* [`similar_names`] Ignore underscore prefixed names
+  [#6403](https://github.com/rust-lang/rust-clippy/pull/6403)
+* [`print_literal`] and [`write_literal`] No longer lint numeric literals
+  [#6408](https://github.com/rust-lang/rust-clippy/pull/6408)
+* [`large_enum_variant`] No longer lints in external macros
+  [#6485](https://github.com/rust-lang/rust-clippy/pull/6485)
+* [`empty_enum`] Only lint if `never_type` feature is enabled
+  [#6513](https://github.com/rust-lang/rust-clippy/pull/6513)
+* [`field_reassign_with_default`] No longer lints in macros
+  [#6553](https://github.com/rust-lang/rust-clippy/pull/6553)
+* [`size_of_in_element_count`] No longer lints when dividing by element size
+  [#6578](https://github.com/rust-lang/rust-clippy/pull/6578)
+* [`needless_return`] No longer lints in macros
+  [#6586](https://github.com/rust-lang/rust-clippy/pull/6586)
+* [`match_overlapping_arm`] No longer lint when first arm is completely included
+  in second arm [#6603](https://github.com/rust-lang/rust-clippy/pull/6603)
+* [`doc_markdown`] Add `WebGL` to the default configuration as an allowed
+  identifier [#6605](https://github.com/rust-lang/rust-clippy/pull/6605)
+
+### Suggestion Fixes/Improvements
+
+* [`field_reassign_with_default`] Don't expand macro in lint suggestion
+  [#6531](https://github.com/rust-lang/rust-clippy/pull/6531)
+* [`match_like_matches_macro`] Strip references in suggestion
+  [#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
+* [`single_match`] Suggest `if` over `if let` when possible
+  [#6574](https://github.com/rust-lang/rust-clippy/pull/6574)
+* [`ref_in_deref`] Use parentheses correctly in suggestion
+  [#6609](https://github.com/rust-lang/rust-clippy/pull/6609)
+* [`stable_sort_primitive`] Clarify error message
+  [#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
+
+### ICE Fixes
+
+* [`zero_sized_map_values`]
+  [#6582](https://github.com/rust-lang/rust-clippy/pull/6582)
+
+### Documentation Improvements
+
+* Improve search performance on the Clippy website and make it possible to
+  directly search for lints on the GitHub issue tracker
+  [#6483](https://github.com/rust-lang/rust-clippy/pull/6483)
+* Clean up `README.md` by removing outdated paragraph
+  [#6488](https://github.com/rust-lang/rust-clippy/pull/6488)
+* [`await_holding_refcell_ref`] and [`await_holding_lock`]
+  [#6585](https://github.com/rust-lang/rust-clippy/pull/6585)
+* [`as_conversions`] [#6608](https://github.com/rust-lang/rust-clippy/pull/6608)
+
+### Others
+
+* Clippy now has a [Roadmap] for 2021. If you like to get involved in a bigger
+  project, take a look at the [Roadmap project page]. All issues listed there
+  are actively mentored
+  [#6462](https://github.com/rust-lang/rust-clippy/pull/6462)
+* The Clippy version number now corresponds to the Rust version number
+  [#6526](https://github.com/rust-lang/rust-clippy/pull/6526)
+* Fix oversight which caused Clippy to lint deps in some environments, where
+  `CLIPPY_TESTS=true` was set somewhere
+  [#6575](https://github.com/rust-lang/rust-clippy/pull/6575)
+* Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
+  [#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
+
+[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/doc/roadmap-2021.md
+[Roadmap project page]: https://github.com/rust-lang/rust-clippy/projects/3
 
 ## Rust 1.50
 
-Current beta, release 2021-02-11
+Current stable, released 2021-02-11
 
-[b20d4c1...4911ab1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4911ab1)
+[b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1)
 
 ### New Lints
 
@@ -90,6 +205,8 @@ Current beta, release 2021-02-11
 * [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]:
   Both now ignore enums with frozen variants
   [#6110](https://github.com/rust-lang/rust-clippy/pull/6110)
+* [`field_reassign_with_default`] No longer lint for private fields
+  [#6537](https://github.com/rust-lang/rust-clippy/pull/6537)
 
 
 ### Suggestion Fixes/Improvements
@@ -137,7 +254,7 @@ Current beta, release 2021-02-11
 
 ## Rust 1.49
 
-Current stable, released 2020-12-31
+Released 2020-12-31
 
 [e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
 
@@ -1910,6 +2027,7 @@ Released 2018-09-13
 [`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
 [`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
 [`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
+[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
 [`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
 [`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
 [`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
@@ -1975,6 +2093,7 @@ Released 2018-09-13
 [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
 [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
 [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
+[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
 [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
 [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
@@ -1990,6 +2109,7 @@ Released 2018-09-13
 [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
 [`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
 [`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
@@ -2042,6 +2162,7 @@ Released 2018-09-13
 [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
 [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
 [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
+[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
 [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
 [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
 [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
index e7755c46eb80dffbe895f141c40b98a495cc9595..ea32a8edd1ffb311848900f19766b08a24ecb8ea 100644 (file)
@@ -37,8 +37,8 @@ tempfile = { version = "3.1.0", optional = true }
 
 [dev-dependencies]
 cargo_metadata = "0.12"
-compiletest_rs = { version = "0.5.0", features = ["tmp"] }
-tester = "0.7"
+compiletest_rs = { version = "0.6.0", features = ["tmp"] }
+tester = "0.9"
 clippy-mini-macro-test = { version = "0.2", path = "mini-macro" }
 serde = { version = "1.0", features = ["derive"] }
 derive-new = "0.5"
index a4928e17e6a947a3bbcb02a3e43ae9575ddc6cf8..3cc03cf360336bc2588b1ba34af53040b19c42de 100644 (file)
@@ -98,6 +98,23 @@ If you want to run Clippy **only** on the given crate, use the `--no-deps` optio
 cargo clippy -p example -- --no-deps 
 ```
 
+### As a rustc replacement (`clippy-driver`)
+
+Clippy can also be used in projects that do not use cargo. To do so, you will need to replace
+your `rustc` compilation commands with `clippy-driver`. For example, if your project runs:
+
+```terminal
+rustc --edition 2018 -Cpanic=abort foo.rs
+```
+
+Then, to enable Clippy, you will need to call:
+
+```terminal
+clippy-driver --edition 2018 -Cpanic=abort foo.rs
+```
+
+Note that `rustc` will still run, i.e. it will still emit the output files it normally does.
+
 ### Travis CI
 
 You can add Clippy to Travis CI in the same way you use it locally:
index 5ac96e2210c89a782685c662482747457c151272..ebf157b80acf1c3aaf8236d145cb16c1977a22a5 100644 (file)
@@ -19,8 +19,9 @@ shell-escape = "0.1"
 tar = { version = "0.4.30", optional = true }
 toml = { version = "0.5", optional = true }
 ureq = { version = "2.0.0-rc3", optional = true }
+rayon = { version = "1.5.0", optional = true }
 walkdir = "2"
 
 [features]
-lintcheck = ["flate2", "serde_json", "tar", "toml", "ureq", "serde", "fs_extra"]
+lintcheck = ["flate2", "serde_json", "tar", "toml", "ureq", "serde", "fs_extra", "rayon"]
 deny-warnings = []
index 3846e8bd4ccb79a8dee8c62f90fea1a99a10661f..a5ed9e27bd2bcba33b723d2c04f404f32f0fce3c 100644 (file)
@@ -1,28 +1,77 @@
-# Clippy Dev Tool 
+# Clippy Dev Tool
 
-The Clippy Dev Tool is a tool to ease Clippy development, similar to `rustc`s `x.py`.
+The Clippy Dev Tool is a tool to ease Clippy development, similar to `rustc`s
+`x.py`.
 
 Functionalities (incomplete):
 
 ## `lintcheck`
-Runs clippy on a fixed set of crates read from `clippy_dev/lintcheck_crates.toml`
-and saves logs of the lint warnings into the repo.
-We can then check the diff and spot new or disappearing warnings.
+
+Runs clippy on a fixed set of crates read from
+`clippy_dev/lintcheck_crates.toml` and saves logs of the lint warnings into the
+repo.  We can then check the diff and spot new or disappearing warnings.
 
 From the repo root, run:
-````
+
+```
 cargo run --target-dir clippy_dev/target --package clippy_dev \
 --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --features lintcheck -- lintcheck
-````
+```
+
 or
-````
+
+```
 cargo dev-lintcheck
-````
+```
 
-By default the logs will be saved into `lintcheck-logs/lintcheck_crates_logs.txt`.
+By default the logs will be saved into
+`lintcheck-logs/lintcheck_crates_logs.txt`.
 
-You can set a custom sources.toml by adding `--crates-toml custom.toml`
-where `custom.toml` must be a relative path from the repo root.
+You can set a custom sources.toml by adding `--crates-toml custom.toml` or using
+`LINTCHECK_TOML="custom.toml"` where `custom.toml` must be a relative path from
+the repo root.
 
 The results will then be saved to `lintcheck-logs/custom_logs.toml`.
 
+### Configuring the Crate Sources
+
+The sources to check are saved in a `toml` file. There are three types of
+sources.
+
+1. Crates-io Source
+
+   ```toml
+   bitflags = {name = "bitflags", versions = ['1.2.1']}
+   ```
+   Requires a "name" and one or multiple "versions" to be checked.
+
+2. `git` Source
+   ````toml
+   puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"}
+   ````
+   Requires a name, the url to the repo and unique identifier of a commit,
+   branch or tag which is checked out before linting.  There is no way to always
+   check `HEAD` because that would lead to changing lint-results as the repo
+   would get updated.  If `git_url` or `git_hash` is missing, an error will be
+   thrown.
+
+3. Local Dependency
+   ```toml
+   clippy = {name = "clippy", path = "/home/user/clippy"}
+   ```
+   For when you want to add a repository that is not published yet.
+
+#### Command Line Options (optional)
+
+```toml
+bitflags = {name = "bitflags", versions = ['1.2.1'], options = ['-Wclippy::pedantic', '-Wclippy::cargo']}
+```
+
+It is possible to specify command line options for each crate. This makes it
+possible to only check a crate for certain lint groups. If no options are
+specified, the lint groups `clippy::all`, `clippy::pedantic`, and
+`clippy::cargo` are checked. If an empty array is specified only `clippy::all`
+is checked.
+
+**Note:** `-Wclippy::all` is always enabled by default, unless `-Aclippy::all`
+is explicitly specified in the options.
index 2a869e9d4491b6805a332495fd753b886a1c821e..c4fa0a9aca70b75333b37f5fe5f6e70decdbe3cf 100644 (file)
@@ -42,9 +42,10 @@ pub fn bless(ignore_timestamp: bool) {
             .for_each(|f| {
                 let test_name = f.path().strip_prefix(test_suite_dir).unwrap();
                 for &ext in &["stdout", "stderr", "fixed"] {
+                    let test_name_ext = format!("stage-id.{}", ext);
                     update_reference_file(
                         f.path().with_extension(ext),
-                        test_name.with_extension(ext),
+                        test_name.with_extension(test_name_ext),
                         ignore_timestamp,
                     );
                 }
index 749a791b280e20f423759f270d2fae53ff671044..b806f5452846505b94b9a00e8666e7752a7a795a 100644 (file)
 
 use std::collections::HashMap;
 use std::process::Command;
-use std::{fmt, fs::write, path::PathBuf};
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::{env, fmt, fs::write, path::PathBuf};
 
 use clap::ArgMatches;
+use rayon::prelude::*;
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
 
-// use this to store the crates when interacting with the crates.toml file
+/// List of sources to check, loaded from a .toml file
 #[derive(Debug, Serialize, Deserialize)]
-struct CrateList {
+struct SourceList {
     crates: HashMap<String, TomlCrate>,
 }
 
-// crate data we stored in the toml, can have multiple versions per crate
-// A single TomlCrate is laster mapped to several CrateSources in that case
+/// A crate source stored inside the .toml
+/// will be translated into on one of the `CrateSource` variants
 #[derive(Debug, Serialize, Deserialize)]
 struct TomlCrate {
     name: String,
@@ -32,27 +34,42 @@ struct TomlCrate {
     git_url: Option<String>,
     git_hash: Option<String>,
     path: Option<String>,
+    options: Option<Vec<String>>,
 }
 
-// represents an archive we download from crates.io, or a git repo, or a local repo
-#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq)]
+/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder
+/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate`
+#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)]
 enum CrateSource {
-    CratesIo { name: String, version: String },
-    Git { name: String, url: String, commit: String },
-    Path { name: String, path: PathBuf },
+    CratesIo {
+        name: String,
+        version: String,
+        options: Option<Vec<String>>,
+    },
+    Git {
+        name: String,
+        url: String,
+        commit: String,
+        options: Option<Vec<String>>,
+    },
+    Path {
+        name: String,
+        path: PathBuf,
+        options: Option<Vec<String>>,
+    },
 }
 
-// represents the extracted sourcecode of a crate
-// we actually don't need to special-case git repos here because it does not matter for clippy, yay!
-// (clippy only needs a simple path)
+/// Represents the actual source code of a crate that we ran "cargo clippy" on
 #[derive(Debug)]
 struct Crate {
     version: String,
     name: String,
     // path to the extracted sources that clippy can check
     path: PathBuf,
+    options: Option<Vec<String>>,
 }
 
+/// A single warning that clippy issued while checking a `Crate`
 #[derive(Debug)]
 struct ClippyWarning {
     crate_name: String,
@@ -62,7 +79,7 @@ struct ClippyWarning {
     column: String,
     linttype: String,
     message: String,
-    ice: bool,
+    is_ice: bool,
 }
 
 impl std::fmt::Display for ClippyWarning {
@@ -76,9 +93,12 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 }
 
 impl CrateSource {
+    /// Makes the sources available on the disk for clippy to check.
+    /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or
+    /// copies a local folder
     fn download_and_extract(&self) -> Crate {
         match self {
-            CrateSource::CratesIo { name, version } => {
+            CrateSource::CratesIo { name, version, options } => {
                 let extract_dir = PathBuf::from("target/lintcheck/crates");
                 let krate_download_dir = PathBuf::from("target/lintcheck/downloads");
 
@@ -110,9 +130,15 @@ fn download_and_extract(&self) -> Crate {
                     version: version.clone(),
                     name: name.clone(),
                     path: extract_dir.join(format!("{}-{}/", name, version)),
+                    options: options.clone(),
                 }
             },
-            CrateSource::Git { name, url, commit } => {
+            CrateSource::Git {
+                name,
+                url,
+                commit,
+                options,
+            } => {
                 let repo_path = {
                     let mut repo_path = PathBuf::from("target/lintcheck/crates");
                     // add a -git suffix in case we have the same crate from crates.io and a git repo
@@ -122,27 +148,37 @@ fn download_and_extract(&self) -> Crate {
                 // clone the repo if we have not done so
                 if !repo_path.is_dir() {
                     println!("Cloning {} and checking out {}", url, commit);
-                    Command::new("git")
+                    if !Command::new("git")
                         .arg("clone")
                         .arg(url)
                         .arg(&repo_path)
-                        .output()
-                        .expect("Failed to clone git repo!");
+                        .status()
+                        .expect("Failed to clone git repo!")
+                        .success()
+                    {
+                        eprintln!("Failed to clone {} into {}", url, repo_path.display())
+                    }
                 }
                 // check out the commit/branch/whatever
-                Command::new("git")
+                if !Command::new("git")
                     .arg("checkout")
                     .arg(commit)
-                    .output()
-                    .expect("Failed to check out commit");
+                    .current_dir(&repo_path)
+                    .status()
+                    .expect("Failed to check out commit")
+                    .success()
+                {
+                    eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display())
+                }
 
                 Crate {
                     version: commit.clone(),
                     name: name.clone(),
                     path: repo_path,
+                    options: options.clone(),
                 }
             },
-            CrateSource::Path { name, path } => {
+            CrateSource::Path { name, path, options } => {
                 use fs_extra::dir;
 
                 // simply copy the entire directory into our target dir
@@ -171,6 +207,7 @@ fn download_and_extract(&self) -> Crate {
                     version: String::from("local"),
                     name: name.clone(),
                     path: crate_root,
+                    options: options.clone(),
                 }
             },
         }
@@ -178,24 +215,56 @@ fn download_and_extract(&self) -> Crate {
 }
 
 impl Crate {
-    fn run_clippy_lints(&self, cargo_clippy_path: &PathBuf) -> Vec<ClippyWarning> {
-        println!("Linting {} {}...", &self.name, &self.version);
+    /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy
+    /// issued
+    fn run_clippy_lints(
+        &self,
+        cargo_clippy_path: &PathBuf,
+        target_dir_index: &AtomicUsize,
+        thread_limit: usize,
+        total_crates_to_lint: usize,
+    ) -> Vec<ClippyWarning> {
+        // advance the atomic index by one
+        let index = target_dir_index.fetch_add(1, Ordering::SeqCst);
+        // "loop" the index within 0..thread_limit
+        let target_dir_index = index % thread_limit;
+        let perc = ((index * 100) as f32 / total_crates_to_lint as f32) as u8;
+
+        if thread_limit == 1 {
+            println!(
+                "{}/{} {}% Linting {} {}",
+                index, total_crates_to_lint, perc, &self.name, &self.version
+            );
+        } else {
+            println!(
+                "{}/{} {}% Linting {} {} in target dir {:?}",
+                index, total_crates_to_lint, perc, &self.name, &self.version, target_dir_index
+            );
+        }
+
         let cargo_clippy_path = std::fs::canonicalize(cargo_clippy_path).unwrap();
 
-        let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir/");
+        let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
+
+        let mut args = vec!["--", "--message-format=json", "--", "--cap-lints=warn"];
+
+        if let Some(options) = &self.options {
+            for opt in options {
+                args.push(opt);
+            }
+        } else {
+            args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"])
+        }
 
         let all_output = std::process::Command::new(&cargo_clippy_path)
-            .env("CARGO_TARGET_DIR", shared_target_dir)
+            // use the looping index to create individual target dirs
+            .env(
+                "CARGO_TARGET_DIR",
+                shared_target_dir.join(format!("_{:?}", target_dir_index)),
+            )
             // lint warnings will look like this:
             // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter`
-            .args(&[
-                "--",
-                "--message-format=json",
-                "--",
-                "--cap-lints=warn",
-                "-Wclippy::pedantic",
-                "-Wclippy::cargo",
-            ])
+            .args(&args)
             .current_dir(&self.path)
             .output()
             .unwrap_or_else(|error| {
@@ -211,28 +280,69 @@ fn run_clippy_lints(&self, cargo_clippy_path: &PathBuf) -> Vec<ClippyWarning> {
         let warnings: Vec<ClippyWarning> = output_lines
             .into_iter()
             // get all clippy warnings and ICEs
-            .filter(|line| line.contains("clippy::") || line.contains("internal compiler error: "))
+            .filter(|line| filter_clippy_warnings(&line))
             .map(|json_msg| parse_json_message(json_msg, &self))
             .collect();
         warnings
     }
 }
 
+/// takes a single json-formatted clippy warnings and returns true (we are interested in that line)
+/// or false (we aren't)
+fn filter_clippy_warnings(line: &str) -> bool {
+    // we want to collect ICEs because clippy might have crashed.
+    // these are summarized later
+    if line.contains("internal compiler error: ") {
+        return true;
+    }
+    // in general, we want all clippy warnings
+    // however due to some kind of bug, sometimes there are absolute paths
+    // to libcore files inside the message
+    // or we end up with cargo-metadata output (https://github.com/rust-lang/rust-clippy/issues/6508)
+
+    // filter out these message to avoid unnecessary noise in the logs
+    if line.contains("clippy::")
+        && !(line.contains("could not read cargo metadata")
+            || (line.contains(".rustup") && line.contains("toolchains")))
+    {
+        return true;
+    }
+    false
+}
+
+/// get the path to lintchecks crate sources .toml file, check LINTCHECK_TOML first but if it's
+/// empty use the default path
+fn lintcheck_config_toml(toml_path: Option<&str>) -> PathBuf {
+    PathBuf::from(
+        env::var("LINTCHECK_TOML").unwrap_or(
+            toml_path
+                .clone()
+                .unwrap_or("clippy_dev/lintcheck_crates.toml")
+                .to_string(),
+        ),
+    )
+}
+
+/// Builds clippy inside the repo to make sure we have a clippy executable we can use.
 fn build_clippy() {
-    Command::new("cargo")
+    let status = Command::new("cargo")
         .arg("build")
-        .output()
+        .status()
         .expect("Failed to build clippy!");
+    if !status.success() {
+        eprintln!("Error: Failed to compile Clippy!");
+        std::process::exit(1);
+    }
 }
 
-// get a list of CrateSources we want to check from a "lintcheck_crates.toml" file.
+/// Read a `toml` file and return a list of `CrateSources` that we want to check with clippy
 fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) {
-    let toml_path = PathBuf::from(toml_path.unwrap_or("clippy_dev/lintcheck_crates.toml"));
+    let toml_path = lintcheck_config_toml(toml_path);
     // save it so that we can use the name of the sources.toml as name for the logfile later.
     let toml_filename = toml_path.file_stem().unwrap().to_str().unwrap().to_string();
     let toml_content: String =
         std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
-    let crate_list: CrateList =
+    let crate_list: SourceList =
         toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e));
     // parse the hashmap of the toml file into a list of crates
     let tomlcrates: Vec<TomlCrate> = crate_list
@@ -249,6 +359,7 @@ fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) {
             crate_sources.push(CrateSource::Path {
                 name: tk.name.clone(),
                 path: PathBuf::from(path),
+                options: tk.options.clone(),
             });
         }
 
@@ -258,6 +369,7 @@ fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) {
                 crate_sources.push(CrateSource::CratesIo {
                     name: tk.name.clone(),
                     version: ver.to_string(),
+                    options: tk.options.clone(),
                 });
             })
         }
@@ -267,6 +379,7 @@ fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) {
                 name: tk.name.clone(),
                 url: tk.git_url.clone().unwrap(),
                 commit: tk.git_hash.clone().unwrap(),
+                options: tk.options.clone(),
             });
         }
         // if we have a version as well as a git data OR only one git data, something is funky
@@ -283,10 +396,13 @@ fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) {
             unreachable!("Failed to translate TomlCrate into CrateSource!");
         }
     });
+    // sort the crates
+    crate_sources.sort();
+
     (toml_filename, crate_sources)
 }
 
-// extract interesting data from a json lint message
+/// Parse the json output of clippy and return a `ClippyWarning`
 fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
     let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e));
 
@@ -307,18 +423,84 @@ fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
             .into(),
         linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(),
         message: jmsg["message"]["message"].to_string().trim_matches('"').into(),
-        ice: json_message.contains("internal compiler error: "),
+        is_ice: json_message.contains("internal compiler error: "),
     }
 }
 
-// the main fn
-pub fn run(clap_config: &ArgMatches) {
-    let cargo_clippy_path: PathBuf = PathBuf::from("target/debug/cargo-clippy");
+/// Generate a short list of occuring lints-types and their count
+fn gather_stats(clippy_warnings: &[ClippyWarning]) -> String {
+    // count lint type occurrences
+    let mut counter: HashMap<&String, usize> = HashMap::new();
+    clippy_warnings
+        .iter()
+        .for_each(|wrn| *counter.entry(&wrn.linttype).or_insert(0) += 1);
 
+    // collect into a tupled list for sorting
+    let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect();
+    // sort by "000{count} {clippy::lintname}"
+    // to not have a lint with 200 and 2 warnings take the same spot
+    stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint));
+
+    stats
+        .iter()
+        .map(|(lint, count)| format!("{} {}\n", lint, count))
+        .collect::<String>()
+}
+
+/// check if the latest modification of the logfile is older than the modification date of the
+/// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck
+fn lintcheck_needs_rerun(toml_path: Option<&str>) -> bool {
+    let clippy_modified: std::time::SystemTime = {
+        let mut times = ["target/debug/clippy-driver", "target/debug/cargo-clippy"]
+            .iter()
+            .map(|p| {
+                std::fs::metadata(p)
+                    .expect("failed to get metadata of file")
+                    .modified()
+                    .expect("failed to get modification date")
+            });
+        // the lates modification of either of the binaries
+        std::cmp::max(times.next().unwrap(), times.next().unwrap())
+    };
+
+    let logs_modified: std::time::SystemTime = std::fs::metadata(lintcheck_config_toml(toml_path))
+        .expect("failed to get metadata of file")
+        .modified()
+        .expect("failed to get modification date");
+
+    // if clippys modification time is bigger (older) than the logs mod time, we need to rerun lintcheck
+    clippy_modified > logs_modified
+}
+
+/// lintchecks `main()` function
+pub fn run(clap_config: &ArgMatches) {
     println!("Compiling clippy...");
     build_clippy();
     println!("Done compiling");
 
+    let clap_toml_path = clap_config.value_of("crates-toml");
+
+    // if the clippy bin is newer than our logs, throw away target dirs to force clippy to
+    // refresh the logs
+    if lintcheck_needs_rerun(clap_toml_path) {
+        let shared_target_dir = "target/lintcheck/shared_target_dir";
+        match std::fs::metadata(&shared_target_dir) {
+            Ok(metadata) => {
+                if metadata.is_dir() {
+                    println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir...");
+                    std::fs::remove_dir_all(&shared_target_dir)
+                        .expect("failed to remove target/lintcheck/shared_target_dir");
+                }
+            },
+            Err(_) => { // dir probably does not exist, don't remove anything
+            },
+        }
+    }
+
+    let cargo_clippy_path: PathBuf = PathBuf::from("target/debug/cargo-clippy")
+        .canonicalize()
+        .expect("failed to canonicalize path to clippy binary");
+
     // assert that clippy is found
     assert!(
         cargo_clippy_path.is_file(),
@@ -335,7 +517,7 @@ pub fn run(clap_config: &ArgMatches) {
     // download and extract the crates, then run clippy on them and collect clippys warnings
     // flatten into one big list of warnings
 
-    let (filename, crates) = read_crates(clap_config.value_of("crates-toml"));
+    let (filename, crates) = read_crates(clap_toml_path);
 
     let clippy_warnings: Vec<ClippyWarning> = if let Some(only_one_crate) = clap_config.value_of("only") {
         // if we don't have the specified crate in the .toml, throw an error
@@ -359,45 +541,60 @@ pub fn run(clap_config: &ArgMatches) {
             .into_iter()
             .map(|krate| krate.download_and_extract())
             .filter(|krate| krate.name == only_one_crate)
-            .map(|krate| krate.run_clippy_lints(&cargo_clippy_path))
+            .map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &AtomicUsize::new(0), 1, 1))
             .flatten()
             .collect()
     } else {
+        let counter = std::sync::atomic::AtomicUsize::new(0);
+
+        // Ask rayon for thread count. Assume that half of that is the number of physical cores
+        // Use one target dir for each core so that we can run N clippys in parallel.
+        // We need to use different target dirs because cargo would lock them for a single build otherwise,
+        // killing the parallelism. However this also means that deps will only be reused half/a
+        // 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
+
+        // by default, use a single thread
+        let num_cpus = match clap_config.value_of("threads") {
+            Some(threads) => {
+                let threads: usize = threads
+                    .parse()
+                    .expect(&format!("Failed to parse '{}' to a digit", threads));
+                if threads == 0 {
+                    // automatic choice
+                    // Rayon seems to return thread count so half that for core count
+                    (rayon::current_num_threads() / 2) as usize
+                } else {
+                    threads
+                }
+            },
+            // no -j passed, use a single thread
+            None => 1,
+        };
+
+        let num_crates = crates.len();
+
         // check all crates (default)
         crates
-            .into_iter()
+            .into_par_iter()
             .map(|krate| krate.download_and_extract())
-            .map(|krate| krate.run_clippy_lints(&cargo_clippy_path))
+            .map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &counter, num_cpus, num_crates))
             .flatten()
             .collect()
     };
 
-    // generate some stats:
+    // generate some stats
+    let stats_formatted = gather_stats(&clippy_warnings);
 
     // grab crashes/ICEs, save the crate name and the ice message
     let ices: Vec<(&String, &String)> = clippy_warnings
         .iter()
-        .filter(|warning| warning.ice)
+        .filter(|warning| warning.is_ice)
         .map(|w| (&w.crate_name, &w.message))
         .collect();
 
-    // count lint type occurrences
-    let mut counter: HashMap<&String, usize> = HashMap::new();
-    clippy_warnings
-        .iter()
-        .for_each(|wrn| *counter.entry(&wrn.linttype).or_insert(0) += 1);
-
-    // collect into a tupled list for sorting
-    let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect();
-    // sort by "000{count} {clippy::lintname}"
-    // to not have a lint with 200 and 2 warnings take the same spot
-    stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint));
-
-    let stats_formatted: String = stats
-        .iter()
-        .map(|(lint, count)| format!("{} {}\n", lint, count))
-        .collect::<String>();
-
     let mut all_msgs: Vec<String> = clippy_warnings.iter().map(|warning| warning.to_string()).collect();
     all_msgs.sort();
     all_msgs.push("\n\n\n\nStats\n\n".into());
@@ -411,5 +608,6 @@ pub fn run(clap_config: &ArgMatches) {
         .for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg)));
 
     let file = format!("lintcheck-logs/{}_logs.txt", filename);
+    println!("Writing logs to {}", file);
     write(file, text).unwrap();
 }
index 5dbd46935a59a88732bcfae41ed9d76914f6a389..505d465760c57873b1c840ba769276e86b78f00e 100644 (file)
@@ -69,6 +69,14 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
                 .value_name("CRATES-SOURCES-TOML-PATH")
                 .long("crates-toml")
                 .help("set the path for a crates.toml where lintcheck should read the sources from"),
+        )
+        .arg(
+            Arg::with_name("threads")
+                .takes_value(true)
+                .value_name("N")
+                .short("j")
+                .long("jobs")
+                .help("number of threads to use, 0 automatic choice"),
         );
 
     let app = App::new("Clippy developer tooling")
index 840341fefc6a2b2053f4b48dd593a4059ae3b795..d5ec8597044c8d83ba2c5ebafeadd930792e761c 100644 (file)
@@ -18,6 +18,7 @@ edition = "2018"
 
 [dependencies]
 cargo_metadata = "0.12"
+clippy_utils = { path = "../clippy_utils" }
 if_chain = "1.0.0"
 itertools = "0.9"
 pulldown-cmark = { version = "0.8", default-features = false }
@@ -38,4 +39,4 @@ syn = { version = "1", features = ["full"] }
 [features]
 deny-warnings = []
 # build clippy with internal lints enabled, off by default
-internal-lints = []
+internal-lints = ["clippy_utils/internal-lints"]
index 4efca10bcdf13d650cdff8067231ae3f8f32d4f5..b53f80fd8bc15801ea0238153a71444e4a657eab 100644 (file)
@@ -1,4 +1,8 @@
-use crate::utils::{differing_macro_contexts, snippet_block_with_applicability, span_lint, span_lint_and_sugg};
+use crate::utils::{
+    differing_macro_contexts, get_parent_expr, get_trait_def_id, implements_trait, paths,
+    snippet_block_with_applicability, span_lint, span_lint_and_sugg,
+};
+use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 use rustc_hir::{BlockCheckMode, Expr, ExprKind};
@@ -52,6 +56,18 @@ impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> {
 
     fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
         if let ExprKind::Closure(_, _, eid, _, _) = expr.kind {
+            // do not lint if the closure is called using an iterator (see #1141)
+            if_chain! {
+                if let Some(parent) = get_parent_expr(self.cx, expr);
+                if let ExprKind::MethodCall(_, _, args, _) = parent.kind;
+                let caller = self.cx.typeck_results().expr_ty(&args[0]);
+                if let Some(iter_id) = get_trait_def_id(self.cx, &paths::ITERATOR);
+                if implements_trait(self.cx, caller, iter_id, &[]);
+                then {
+                    return;
+                }
+            }
+
             let body = self.cx.tcx.hir().body(eid);
             let ex = &body.value;
             if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() {
index 93ccc76d0c9cdf829ed63b09cae07f21ad6c073d..34f0e6ab027053840dbc0181063e492bc58e58d3 100644 (file)
@@ -122,6 +122,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
         if let ast::ExprKind::Block(ref block, _) = else_.kind;
         if !block_starts_with_comment(cx, block);
         if let Some(else_) = expr_block(block);
+        if else_.attrs.is_empty();
         if !else_.span.from_expansion();
         if let ast::ExprKind::If(..) = else_.kind;
         then {
@@ -143,16 +144,12 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
     if_chain! {
         if !block_starts_with_comment(cx, then);
         if let Some(inner) = expr_block(then);
+        if inner.attrs.is_empty();
         if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind;
+        // Prevent triggering on `if c { if let a = b { .. } }`.
+        if !matches!(check_inner.kind, ast::ExprKind::Let(..));
+        if expr.span.ctxt() == inner.span.ctxt();
         then {
-            if let ast::ExprKind::Let(..) = check_inner.kind {
-                // Prevent triggering on `if c { if let a = b { .. } }`.
-                return;
-            }
-
-            if expr.span.ctxt() != inner.span.ctxt() {
-                return;
-            }
             span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| {
                 let lhs = Sugg::ast(cx, check, "..");
                 let rhs = Sugg::ast(cx, check_inner, "..");
index 67282cb7900579bd399986ab5f1aef029ac1b4c8..3c45525684be455365a85c323d076ffbe44d92e3 100644 (file)
@@ -96,12 +96,12 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
                 cx,
                 COLLAPSIBLE_MATCH,
                 expr.span,
-                "Unnecessary nested match",
+                "unnecessary nested match",
                 |diag| {
                     let mut help_span = MultiSpan::from_spans(vec![binding_span, non_wild_inner_arm.pat.span]);
-                    help_span.push_span_label(binding_span, "Replace this binding".into());
+                    help_span.push_span_label(binding_span, "replace this binding".into());
                     help_span.push_span_label(non_wild_inner_arm.pat.span, "with this pattern".into());
-                    diag.span_help(help_span, "The outer pattern can be modified to include the inner pattern.");
+                    diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern");
                 },
             );
         }
index 1b89d0bbe3862cebfee0e5a7c071f35fcef8997e..7e87f53e3fba7d2a7c8b739689d9dc416b77810e 100644 (file)
@@ -1,574 +1 @@
-#![allow(clippy::float_cmp)]
-
-use crate::utils::{clip, sext, unsext};
-use if_chain::if_chain;
-use rustc_ast::ast::{self, LitFloatType, LitKind};
-use rustc_data_structures::sync::Lrc;
-use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
-use rustc_lint::LateContext;
-use rustc_middle::mir::interpret::Scalar;
-use rustc_middle::ty::subst::{Subst, SubstsRef};
-use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt};
-use rustc_middle::{bug, span_bug};
-use rustc_span::symbol::Symbol;
-use std::cmp::Ordering::{self, Equal};
-use std::convert::TryInto;
-use std::hash::{Hash, Hasher};
-
-/// A `LitKind`-like enum to fold constant `Expr`s into.
-#[derive(Debug, Clone)]
-pub enum Constant {
-    /// A `String` (e.g., "abc").
-    Str(String),
-    /// A binary string (e.g., `b"abc"`).
-    Binary(Lrc<[u8]>),
-    /// A single `char` (e.g., `'a'`).
-    Char(char),
-    /// An integer's bit representation.
-    Int(u128),
-    /// An `f32`.
-    F32(f32),
-    /// An `f64`.
-    F64(f64),
-    /// `true` or `false`.
-    Bool(bool),
-    /// An array of constants.
-    Vec(Vec<Constant>),
-    /// Also an array, but with only one constant, repeated N times.
-    Repeat(Box<Constant>, u64),
-    /// A tuple of constants.
-    Tuple(Vec<Constant>),
-    /// A raw pointer.
-    RawPtr(u128),
-    /// A reference
-    Ref(Box<Constant>),
-    /// A literal with syntax error.
-    Err(Symbol),
-}
-
-impl PartialEq for Constant {
-    fn eq(&self, other: &Self) -> bool {
-        match (self, other) {
-            (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
-            (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
-            (&Self::Char(l), &Self::Char(r)) => l == r,
-            (&Self::Int(l), &Self::Int(r)) => l == r,
-            (&Self::F64(l), &Self::F64(r)) => {
-                // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
-                // `Fw32 == Fw64`, so don’t compare them.
-                // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
-                l.to_bits() == r.to_bits()
-            },
-            (&Self::F32(l), &Self::F32(r)) => {
-                // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
-                // `Fw32 == Fw64`, so don’t compare them.
-                // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
-                f64::from(l).to_bits() == f64::from(r).to_bits()
-            },
-            (&Self::Bool(l), &Self::Bool(r)) => l == r,
-            (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
-            (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
-            (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb,
-            // TODO: are there inter-type equalities?
-            _ => false,
-        }
-    }
-}
-
-impl Hash for Constant {
-    fn hash<H>(&self, state: &mut H)
-    where
-        H: Hasher,
-    {
-        std::mem::discriminant(self).hash(state);
-        match *self {
-            Self::Str(ref s) => {
-                s.hash(state);
-            },
-            Self::Binary(ref b) => {
-                b.hash(state);
-            },
-            Self::Char(c) => {
-                c.hash(state);
-            },
-            Self::Int(i) => {
-                i.hash(state);
-            },
-            Self::F32(f) => {
-                f64::from(f).to_bits().hash(state);
-            },
-            Self::F64(f) => {
-                f.to_bits().hash(state);
-            },
-            Self::Bool(b) => {
-                b.hash(state);
-            },
-            Self::Vec(ref v) | Self::Tuple(ref v) => {
-                v.hash(state);
-            },
-            Self::Repeat(ref c, l) => {
-                c.hash(state);
-                l.hash(state);
-            },
-            Self::RawPtr(u) => {
-                u.hash(state);
-            },
-            Self::Ref(ref r) => {
-                r.hash(state);
-            },
-            Self::Err(ref s) => {
-                s.hash(state);
-            },
-        }
-    }
-}
-
-impl Constant {
-    pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
-        match (left, right) {
-            (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
-            (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
-            (&Self::Int(l), &Self::Int(r)) => {
-                if let ty::Int(int_ty) = *cmp_type.kind() {
-                    Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
-                } else {
-                    Some(l.cmp(&r))
-                }
-            },
-            (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
-            (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
-            (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
-            (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => l
-                .iter()
-                .zip(r.iter())
-                .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
-                .find(|r| r.map_or(true, |o| o != Ordering::Equal))
-                .unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
-            (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
-                match Self::partial_cmp(tcx, cmp_type, lv, rv) {
-                    Some(Equal) => Some(ls.cmp(rs)),
-                    x => x,
-                }
-            },
-            (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb),
-            // TODO: are there any useful inter-type orderings?
-            _ => None,
-        }
-    }
-}
-
-/// Parses a `LitKind` to a `Constant`.
-pub fn lit_to_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
-    match *lit {
-        LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
-        LitKind::Byte(b) => Constant::Int(u128::from(b)),
-        LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)),
-        LitKind::Char(c) => Constant::Char(c),
-        LitKind::Int(n, _) => Constant::Int(n),
-        LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
-            ast::FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()),
-            ast::FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()),
-        },
-        LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() {
-            ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
-            ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
-            _ => bug!(),
-        },
-        LitKind::Bool(b) => Constant::Bool(b),
-        LitKind::Err(s) => Constant::Err(s),
-    }
-}
-
-pub fn constant<'tcx>(
-    lcx: &LateContext<'tcx>,
-    typeck_results: &ty::TypeckResults<'tcx>,
-    e: &Expr<'_>,
-) -> Option<(Constant, bool)> {
-    let mut cx = ConstEvalLateContext {
-        lcx,
-        typeck_results,
-        param_env: lcx.param_env,
-        needed_resolution: false,
-        substs: lcx.tcx.intern_substs(&[]),
-    };
-    cx.expr(e).map(|cst| (cst, cx.needed_resolution))
-}
-
-pub fn constant_simple<'tcx>(
-    lcx: &LateContext<'tcx>,
-    typeck_results: &ty::TypeckResults<'tcx>,
-    e: &Expr<'_>,
-) -> Option<Constant> {
-    constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
-}
-
-/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`.
-pub fn constant_context<'a, 'tcx>(
-    lcx: &'a LateContext<'tcx>,
-    typeck_results: &'a ty::TypeckResults<'tcx>,
-) -> ConstEvalLateContext<'a, 'tcx> {
-    ConstEvalLateContext {
-        lcx,
-        typeck_results,
-        param_env: lcx.param_env,
-        needed_resolution: false,
-        substs: lcx.tcx.intern_substs(&[]),
-    }
-}
-
-pub struct ConstEvalLateContext<'a, 'tcx> {
-    lcx: &'a LateContext<'tcx>,
-    typeck_results: &'a ty::TypeckResults<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    needed_resolution: bool,
-    substs: SubstsRef<'tcx>,
-}
-
-impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
-    /// Simple constant folding: Insert an expression, get a constant or none.
-    pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
-        match e.kind {
-            ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
-            ExprKind::Block(ref block, _) => self.block(block),
-            ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e))),
-            ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec),
-            ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple),
-            ExprKind::Repeat(ref value, _) => {
-                let n = match self.typeck_results.expr_ty(e).kind() {
-                    ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?,
-                    _ => span_bug!(e.span, "typeck error"),
-                };
-                self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
-            },
-            ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op {
-                UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)),
-                UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
-                UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
-            }),
-            ExprKind::If(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
-            ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right),
-            ExprKind::Call(ref callee, ref args) => {
-                // We only handle a few const functions for now.
-                if_chain! {
-                    if args.is_empty();
-                    if let ExprKind::Path(qpath) = &callee.kind;
-                    let res = self.typeck_results.qpath_res(qpath, callee.hir_id);
-                    if let Some(def_id) = res.opt_def_id();
-                    let def_path: Vec<_> = self.lcx.get_def_path(def_id).into_iter().map(Symbol::as_str).collect();
-                    let def_path: Vec<&str> = def_path.iter().take(4).map(|s| &**s).collect();
-                    if let ["core", "num", int_impl, "max_value"] = *def_path;
-                    then {
-                       let value = match int_impl {
-                           "<impl i8>" => i8::MAX as u128,
-                           "<impl i16>" => i16::MAX as u128,
-                           "<impl i32>" => i32::MAX as u128,
-                           "<impl i64>" => i64::MAX as u128,
-                           "<impl i128>" => i128::MAX as u128,
-                           _ => return None,
-                       };
-                       Some(Constant::Int(value))
-                    }
-                    else {
-                        None
-                    }
-                }
-            },
-            ExprKind::Index(ref arr, ref index) => self.index(arr, index),
-            ExprKind::AddrOf(_, _, ref inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
-            // TODO: add other expressions.
-            _ => None,
-        }
-    }
-
-    #[allow(clippy::cast_possible_wrap)]
-    fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
-        use self::Constant::{Bool, Int};
-        match *o {
-            Bool(b) => Some(Bool(!b)),
-            Int(value) => {
-                let value = !value;
-                match *ty.kind() {
-                    ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
-                    ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
-                    _ => None,
-                }
-            },
-            _ => None,
-        }
-    }
-
-    fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
-        use self::Constant::{Int, F32, F64};
-        match *o {
-            Int(value) => {
-                let ity = match *ty.kind() {
-                    ty::Int(ity) => ity,
-                    _ => return None,
-                };
-                // sign extend
-                let value = sext(self.lcx.tcx, value, ity);
-                let value = value.checked_neg()?;
-                // clear unused bits
-                Some(Int(unsext(self.lcx.tcx, value, ity)))
-            },
-            F32(f) => Some(F32(-f)),
-            F64(f) => Some(F64(-f)),
-            _ => None,
-        }
-    }
-
-    /// Create `Some(Vec![..])` of all constants, unless there is any
-    /// non-constant part.
-    fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
-        vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
-    }
-
-    /// Lookup a possibly constant expression from a `ExprKind::Path`.
-    fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant> {
-        let res = self.typeck_results.qpath_res(qpath, id);
-        match res {
-            Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
-                let substs = self.typeck_results.node_substs(id);
-                let substs = if self.substs.is_empty() {
-                    substs
-                } else {
-                    substs.subst(self.lcx.tcx, self.substs)
-                };
-
-                let result = self
-                    .lcx
-                    .tcx
-                    .const_eval_resolve(
-                        self.param_env,
-                        ty::WithOptConstParam::unknown(def_id),
-                        substs,
-                        None,
-                        None,
-                    )
-                    .ok()
-                    .map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?;
-                let result = miri_to_const(&result);
-                if result.is_some() {
-                    self.needed_resolution = true;
-                }
-                result
-            },
-            // FIXME: cover all usable cases.
-            _ => None,
-        }
-    }
-
-    fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant> {
-        let lhs = self.expr(lhs);
-        let index = self.expr(index);
-
-        match (lhs, index) {
-            (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) {
-                Some(Constant::F32(x)) => Some(Constant::F32(*x)),
-                Some(Constant::F64(x)) => Some(Constant::F64(*x)),
-                _ => None,
-            },
-            (Some(Constant::Vec(vec)), _) => {
-                if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) {
-                    match vec.get(0) {
-                        Some(Constant::F32(x)) => Some(Constant::F32(*x)),
-                        Some(Constant::F64(x)) => Some(Constant::F64(*x)),
-                        _ => None,
-                    }
-                } else {
-                    None
-                }
-            },
-            _ => None,
-        }
-    }
-
-    /// A block can only yield a constant if it only has one constant expression.
-    fn block(&mut self, block: &Block<'_>) -> Option<Constant> {
-        if block.stmts.is_empty() {
-            block.expr.as_ref().and_then(|b| self.expr(b))
-        } else {
-            None
-        }
-    }
-
-    fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant> {
-        if let Some(Constant::Bool(b)) = self.expr(cond) {
-            if b {
-                self.expr(&*then)
-            } else {
-                otherwise.as_ref().and_then(|expr| self.expr(expr))
-            }
-        } else {
-            None
-        }
-    }
-
-    fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
-        let l = self.expr(left)?;
-        let r = self.expr(right);
-        match (l, r) {
-            (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() {
-                ty::Int(ity) => {
-                    let l = sext(self.lcx.tcx, l, ity);
-                    let r = sext(self.lcx.tcx, r, ity);
-                    let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity));
-                    match op.node {
-                        BinOpKind::Add => l.checked_add(r).map(zext),
-                        BinOpKind::Sub => l.checked_sub(r).map(zext),
-                        BinOpKind::Mul => l.checked_mul(r).map(zext),
-                        BinOpKind::Div if r != 0 => l.checked_div(r).map(zext),
-                        BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext),
-                        BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext),
-                        BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext),
-                        BinOpKind::BitXor => Some(zext(l ^ r)),
-                        BinOpKind::BitOr => Some(zext(l | r)),
-                        BinOpKind::BitAnd => Some(zext(l & r)),
-                        BinOpKind::Eq => Some(Constant::Bool(l == r)),
-                        BinOpKind::Ne => Some(Constant::Bool(l != r)),
-                        BinOpKind::Lt => Some(Constant::Bool(l < r)),
-                        BinOpKind::Le => Some(Constant::Bool(l <= r)),
-                        BinOpKind::Ge => Some(Constant::Bool(l >= r)),
-                        BinOpKind::Gt => Some(Constant::Bool(l > r)),
-                        _ => None,
-                    }
-                },
-                ty::Uint(_) => match op.node {
-                    BinOpKind::Add => l.checked_add(r).map(Constant::Int),
-                    BinOpKind::Sub => l.checked_sub(r).map(Constant::Int),
-                    BinOpKind::Mul => l.checked_mul(r).map(Constant::Int),
-                    BinOpKind::Div => l.checked_div(r).map(Constant::Int),
-                    BinOpKind::Rem => l.checked_rem(r).map(Constant::Int),
-                    BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int),
-                    BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int),
-                    BinOpKind::BitXor => Some(Constant::Int(l ^ r)),
-                    BinOpKind::BitOr => Some(Constant::Int(l | r)),
-                    BinOpKind::BitAnd => Some(Constant::Int(l & r)),
-                    BinOpKind::Eq => Some(Constant::Bool(l == r)),
-                    BinOpKind::Ne => Some(Constant::Bool(l != r)),
-                    BinOpKind::Lt => Some(Constant::Bool(l < r)),
-                    BinOpKind::Le => Some(Constant::Bool(l <= r)),
-                    BinOpKind::Ge => Some(Constant::Bool(l >= r)),
-                    BinOpKind::Gt => Some(Constant::Bool(l > r)),
-                    _ => None,
-                },
-                _ => None,
-            },
-            (Constant::F32(l), Some(Constant::F32(r))) => match op.node {
-                BinOpKind::Add => Some(Constant::F32(l + r)),
-                BinOpKind::Sub => Some(Constant::F32(l - r)),
-                BinOpKind::Mul => Some(Constant::F32(l * r)),
-                BinOpKind::Div => Some(Constant::F32(l / r)),
-                BinOpKind::Rem => Some(Constant::F32(l % r)),
-                BinOpKind::Eq => Some(Constant::Bool(l == r)),
-                BinOpKind::Ne => Some(Constant::Bool(l != r)),
-                BinOpKind::Lt => Some(Constant::Bool(l < r)),
-                BinOpKind::Le => Some(Constant::Bool(l <= r)),
-                BinOpKind::Ge => Some(Constant::Bool(l >= r)),
-                BinOpKind::Gt => Some(Constant::Bool(l > r)),
-                _ => None,
-            },
-            (Constant::F64(l), Some(Constant::F64(r))) => match op.node {
-                BinOpKind::Add => Some(Constant::F64(l + r)),
-                BinOpKind::Sub => Some(Constant::F64(l - r)),
-                BinOpKind::Mul => Some(Constant::F64(l * r)),
-                BinOpKind::Div => Some(Constant::F64(l / r)),
-                BinOpKind::Rem => Some(Constant::F64(l % r)),
-                BinOpKind::Eq => Some(Constant::Bool(l == r)),
-                BinOpKind::Ne => Some(Constant::Bool(l != r)),
-                BinOpKind::Lt => Some(Constant::Bool(l < r)),
-                BinOpKind::Le => Some(Constant::Bool(l <= r)),
-                BinOpKind::Ge => Some(Constant::Bool(l >= r)),
-                BinOpKind::Gt => Some(Constant::Bool(l > r)),
-                _ => None,
-            },
-            (l, r) => match (op.node, l, r) {
-                (BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)),
-                (BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)),
-                (BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => {
-                    Some(r)
-                },
-                (BinOpKind::BitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)),
-                (BinOpKind::BitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)),
-                (BinOpKind::BitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)),
-                _ => None,
-            },
-        }
-    }
-}
-
-pub fn miri_to_const(result: &ty::Const<'_>) -> Option<Constant> {
-    use rustc_middle::mir::interpret::ConstValue;
-    match result.val {
-        ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => {
-            match result.ty.kind() {
-                ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
-                ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
-                ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
-                    int.try_into().expect("invalid f32 bit representation"),
-                ))),
-                ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
-                    int.try_into().expect("invalid f64 bit representation"),
-                ))),
-                ty::RawPtr(type_and_mut) => {
-                    if let ty::Uint(_) = type_and_mut.ty.kind() {
-                        return Some(Constant::RawPtr(int.assert_bits(int.size())));
-                    }
-                    None
-                },
-                // FIXME: implement other conversions.
-                _ => None,
-            }
-        },
-        ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind() {
-            ty::Ref(_, tam, _) => match tam.kind() {
-                ty::Str => String::from_utf8(
-                    data.inspect_with_uninit_and_ptr_outside_interpreter(start..end)
-                        .to_owned(),
-                )
-                .ok()
-                .map(Constant::Str),
-                _ => None,
-            },
-            _ => None,
-        },
-        ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind() {
-            ty::Array(sub_type, len) => match sub_type.kind() {
-                ty::Float(FloatTy::F32) => match miri_to_const(len) {
-                    Some(Constant::Int(len)) => alloc
-                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize))
-                        .to_owned()
-                        .chunks(4)
-                        .map(|chunk| {
-                            Some(Constant::F32(f32::from_le_bytes(
-                                chunk.try_into().expect("this shouldn't happen"),
-                            )))
-                        })
-                        .collect::<Option<Vec<Constant>>>()
-                        .map(Constant::Vec),
-                    _ => None,
-                },
-                ty::Float(FloatTy::F64) => match miri_to_const(len) {
-                    Some(Constant::Int(len)) => alloc
-                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize))
-                        .to_owned()
-                        .chunks(8)
-                        .map(|chunk| {
-                            Some(Constant::F64(f64::from_le_bytes(
-                                chunk.try_into().expect("this shouldn't happen"),
-                            )))
-                        })
-                        .collect::<Option<Vec<Constant>>>()
-                        .map(Constant::Vec),
-                    _ => None,
-                },
-                // FIXME: implement other array type conversions.
-                _ => None,
-            },
-            _ => None,
-        },
-        // FIXME: implement other conversions.
-        _ => None,
-    }
-}
+pub use clippy_utils::consts::*;
diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
new file mode 100644 (file)
index 0000000..6ace9aa
--- /dev/null
@@ -0,0 +1,237 @@
+use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
+use rustc_errors::Applicability;
+use rustc_hir::{
+    intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor},
+    Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind,
+};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::{
+    hir::map::Map,
+    ty::{self, FloatTy, IntTy, PolyFnSig, Ty},
+};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+use if_chain::if_chain;
+
+use crate::utils::{snippet, span_lint_and_sugg};
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type
+    /// inference.
+    ///
+    /// Default numeric fallback means that if numeric types have not yet been bound to concrete
+    /// types at the end of type inference, then integer type is bound to `i32`, and similarly
+    /// floating type is bound to `f64`.
+    ///
+    /// See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.
+    ///
+    /// **Why is this bad?** For those who are very careful about types, default numeric fallback
+    /// can be a pitfall that cause unexpected runtime behavior.
+    ///
+    /// **Known problems:** This lint can only be allowed at the function level or above.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// let i = 10;
+    /// let f = 1.23;
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let i = 10i32;
+    /// let f = 1.23f64;
+    /// ```
+    pub DEFAULT_NUMERIC_FALLBACK,
+    restriction,
+    "usage of unconstrained numeric literals which may cause default numeric fallback."
+}
+
+declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
+
+impl LateLintPass<'_> for DefaultNumericFallback {
+    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
+        let mut visitor = NumericFallbackVisitor::new(cx);
+        visitor.visit_body(body);
+    }
+}
+
+struct NumericFallbackVisitor<'a, 'tcx> {
+    /// Stack manages type bound of exprs. The top element holds current expr type.
+    ty_bounds: Vec<TyBound<'tcx>>,
+
+    cx: &'a LateContext<'tcx>,
+}
+
+impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
+    fn new(cx: &'a LateContext<'tcx>) -> Self {
+        Self {
+            ty_bounds: vec![TyBound::Nothing],
+            cx,
+        }
+    }
+
+    /// Check whether a passed literal has potential to cause fallback or not.
+    fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
+        if_chain! {
+                if let Some(ty_bound) = self.ty_bounds.last();
+                if matches!(lit.node,
+                            LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
+                if !ty_bound.is_integral();
+                then {
+                    let suffix = match lit_ty.kind() {
+                        ty::Int(IntTy::I32) => "i32",
+                        ty::Float(FloatTy::F64) => "f64",
+                        // Default numeric fallback never results in other types.
+                        _ => return,
+                    };
+
+                    let sugg = format!("{}_{}", snippet(self.cx, lit.span, ""), suffix);
+                    span_lint_and_sugg(
+                        self.cx,
+                        DEFAULT_NUMERIC_FALLBACK,
+                        lit.span,
+                        "default numeric fallback might occur",
+                        "consider adding suffix",
+                        sugg,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+        }
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
+    type Map = Map<'tcx>;
+
+    #[allow(clippy::too_many_lines)]
+    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+        match &expr.kind {
+            ExprKind::Call(func, args) => {
+                if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) {
+                    for (expr, bound) in args.iter().zip(fn_sig.skip_binder().inputs().iter()) {
+                        // Push found arg type, then visit arg.
+                        self.ty_bounds.push(TyBound::Ty(bound));
+                        self.visit_expr(expr);
+                        self.ty_bounds.pop();
+                    }
+                    return;
+                }
+            },
+
+            ExprKind::MethodCall(_, _, args, _) => {
+                if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
+                    let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
+                    for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) {
+                        self.ty_bounds.push(TyBound::Ty(bound));
+                        self.visit_expr(expr);
+                        self.ty_bounds.pop();
+                    }
+                    return;
+                }
+            },
+
+            ExprKind::Struct(qpath, fields, base) => {
+                if_chain! {
+                    if let Some(def_id) = self.cx.qpath_res(qpath, expr.hir_id).opt_def_id();
+                    let ty = self.cx.tcx.type_of(def_id);
+                    if let Some(adt_def) = ty.ty_adt_def();
+                    if adt_def.is_struct();
+                    if let Some(variant) = adt_def.variants.iter().next();
+                    then {
+                        let fields_def = &variant.fields;
+
+                        // Push field type then visit each field expr.
+                        for field in fields.iter() {
+                            let bound =
+                                fields_def
+                                    .iter()
+                                    .find_map(|f_def| {
+                                        if f_def.ident == field.ident
+                                            { Some(self.cx.tcx.type_of(f_def.did)) }
+                                        else { None }
+                                    });
+                            self.ty_bounds.push(bound.into());
+                            self.visit_expr(field.expr);
+                            self.ty_bounds.pop();
+                        }
+
+                        // Visit base with no bound.
+                        if let Some(base) = base {
+                            self.ty_bounds.push(TyBound::Nothing);
+                            self.visit_expr(base);
+                            self.ty_bounds.pop();
+                        }
+                        return;
+                    }
+                }
+            },
+
+            ExprKind::Lit(lit) => {
+                let ty = self.cx.typeck_results().expr_ty(expr);
+                self.check_lit(lit, ty);
+                return;
+            },
+
+            _ => {},
+        }
+
+        walk_expr(self, expr);
+    }
+
+    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
+        match stmt.kind {
+            StmtKind::Local(local) => {
+                if local.ty.is_some() {
+                    self.ty_bounds.push(TyBound::Any)
+                } else {
+                    self.ty_bounds.push(TyBound::Nothing)
+                }
+            },
+
+            _ => self.ty_bounds.push(TyBound::Nothing),
+        }
+
+        walk_stmt(self, stmt);
+        self.ty_bounds.pop();
+    }
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+}
+
+fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<PolyFnSig<'tcx>> {
+    let node_ty = cx.typeck_results().node_type_opt(hir_id)?;
+    // We can't use `TyS::fn_sig` because it automatically performs substs, this may result in FNs.
+    match node_ty.kind() {
+        ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id)),
+        ty::FnPtr(fn_sig) => Some(*fn_sig),
+        _ => None,
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+enum TyBound<'tcx> {
+    Any,
+    Ty(Ty<'tcx>),
+    Nothing,
+}
+
+impl<'tcx> TyBound<'tcx> {
+    fn is_integral(self) -> bool {
+        match self {
+            TyBound::Any => true,
+            TyBound::Ty(t) => t.is_integral(),
+            TyBound::Nothing => false,
+        }
+    }
+}
+
+impl<'tcx> From<Option<Ty<'tcx>>> for TyBound<'tcx> {
+    fn from(v: Option<Ty<'tcx>>) -> Self {
+        match v {
+            Some(t) => TyBound::Ty(t),
+            None => TyBound::Nothing,
+        }
+    }
+}
index 67b7cf91958655e65e93e671a3e3ac1499f67acc..39a202f281cb7d2f8908cff1d8ac402e0d4c2c99 100644 (file)
@@ -1,6 +1,6 @@
 use crate::utils::{
-    implements_trait, is_entrypoint_fn, is_type_diagnostic_item, match_panic_def_id, method_chain_args, return_ty,
-    span_lint, span_lint_and_note,
+    implements_trait, is_entrypoint_fn, is_expn_of, is_type_diagnostic_item, match_panic_def_id, method_chain_args,
+    return_ty, span_lint, span_lint_and_note,
 };
 use if_chain::if_chain;
 use itertools::Itertools;
@@ -216,9 +216,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
         let headers = check_attrs(cx, &self.valid_idents, &item.attrs);
         match item.kind {
             hir::ItemKind::Fn(ref sig, _, body_id) => {
-                if !(is_entrypoint_fn(cx, item.def_id.to_def_id())
-                    || in_external_macro(cx.tcx.sess, item.span))
-                {
+                if !(is_entrypoint_fn(cx, item.def_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
                     let body = cx.tcx.hir().body(body_id);
                     let mut fpu = FindPanicUnwrap {
                         cx,
@@ -226,7 +224,15 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
                         panic_span: None,
                     };
                     fpu.visit_expr(&body.value);
-                    lint_for_missing_headers(cx, item.hir_id(), item.span, sig, headers, Some(body_id), fpu.panic_span);
+                    lint_for_missing_headers(
+                        cx,
+                        item.hir_id(),
+                        item.span,
+                        sig,
+                        headers,
+                        Some(body_id),
+                        fpu.panic_span,
+                    );
                 }
             },
             hir::ItemKind::Impl(ref impl_) => {
@@ -264,7 +270,15 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<
                 panic_span: None,
             };
             fpu.visit_expr(&body.value);
-            lint_for_missing_headers(cx, item.hir_id(), item.span, sig, headers, Some(body_id), fpu.panic_span);
+            lint_for_missing_headers(
+                cx,
+                item.hir_id(),
+                item.span,
+                sig,
+                headers,
+                Some(body_id),
+                fpu.panic_span,
+            );
         }
     }
 }
@@ -561,9 +575,7 @@ fn has_needless_main(code: &str, edition: Edition) -> bool {
                             | ItemKind::ExternCrate(..)
                             | ItemKind::ForeignMod(..) => return false,
                             // We found a main function ...
-                            ItemKind::Fn(box FnKind(_, sig, _, Some(block)))
-                                if item.ident.name == sym::main =>
-                            {
+                            ItemKind::Fn(box FnKind(_, sig, _, 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,
@@ -699,6 +711,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
             if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind;
             if let Some(path_def_id) = path.res.opt_def_id();
             if match_panic_def_id(self.cx, path_def_id);
+            if is_expn_of(expr.span, "unreachable").is_none();
             then {
                 self.panic_span = Some(expr.span);
             }
diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
new file mode 100644 (file)
index 0000000..0933f98
--- /dev/null
@@ -0,0 +1,101 @@
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{def, Expr, ExprKind, PrimTy, QPath, TyKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::Ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::sym;
+
+use crate::utils::is_type_diagnostic_item;
+use crate::utils::span_lint_and_sugg;
+use crate::utils::sugg::Sugg;
+
+declare_clippy_lint! {
+    /// **What it does:**
+    /// Checks for function invocations of the form `primitive::from_str_radix(s, 10)`
+    ///
+    /// **Why is this bad?**
+    /// This specific common use case can be rewritten as `s.parse::<primitive>()`
+    /// (and in most cases, the turbofish can be removed), which reduces code length
+    /// and complexity.
+    ///
+    /// **Known problems:**
+    /// This lint may suggest using (&<expression>).parse() instead of <expression>.parse() directly
+    /// in some cases, which is correct but adds unnecessary complexity to the code.
+    ///
+    /// **Example:**
+    ///
+    /// ```ignore
+    /// let input: &str = get_input();
+    /// let num = u16::from_str_radix(input, 10)?;
+    /// ```
+    /// Use instead:
+    /// ```ignore
+    /// let input: &str = get_input();
+    /// let num: u16 = input.parse()?;
+    /// ```
+    pub FROM_STR_RADIX_10,
+    style,
+    "from_str_radix with radix 10"
+}
+
+declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]);
+
+impl LateLintPass<'tcx> for FromStrRadix10 {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) {
+        if_chain! {
+            if let ExprKind::Call(maybe_path, arguments) = &exp.kind;
+            if let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind;
+
+            // check if the first part of the path is some integer primitive
+            if let TyKind::Path(ty_qpath) = &ty.kind;
+            let ty_res = cx.qpath_res(ty_qpath, ty.hir_id);
+            if let def::Res::PrimTy(prim_ty) = ty_res;
+            if matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_));
+
+            // check if the second part of the path indeed calls the associated
+            // function `from_str_radix`
+            if pathseg.ident.name.as_str() == "from_str_radix";
+
+            // check if the second argument is a primitive `10`
+            if arguments.len() == 2;
+            if let ExprKind::Lit(lit) = &arguments[1].kind;
+            if let rustc_ast::ast::LitKind::Int(10, _) = lit.node;
+
+            then {
+                let expr = if let ExprKind::AddrOf(_, _, expr) = &arguments[0].kind {
+                    let ty = cx.typeck_results().expr_ty(expr);
+                    if is_ty_stringish(cx, ty) {
+                        expr
+                    } else {
+                        &arguments[0]
+                    }
+                } else {
+                    &arguments[0]
+                };
+
+                let sugg = Sugg::hir_with_applicability(
+                    cx,
+                    expr,
+                    "<string>",
+                    &mut Applicability::MachineApplicable
+                ).maybe_par();
+
+                span_lint_and_sugg(
+                    cx,
+                    FROM_STR_RADIX_10,
+                    exp.span,
+                    "this call to `from_str_radix` can be replaced with a call to `str::parse`",
+                    "try",
+                    format!("{}.parse::<{}>()", sugg, prim_ty.name_str()),
+                    Applicability::MaybeIncorrect
+                );
+            }
+        }
+    }
+}
+
+/// Checks if a Ty is `String` or `&str`
+fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
+    is_type_diagnostic_item(cx, ty, sym::string_type) || is_type_diagnostic_item(cx, ty, sym::str)
+}
index f770f1f812a9dba875ce3a94bf626f226acb9a50..e4b3a9009f612664916c01b864699c9edcbe8961 100644 (file)
@@ -1,7 +1,7 @@
 use crate::utils::{
     attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats,
-    last_path_segment, match_def_path, must_use_attr, path_to_local, return_ty, snippet, snippet_opt, span_lint,
-    span_lint_and_help, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function,
+    match_def_path, must_use_attr, path_to_local, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help,
+    span_lint_and_then, trait_ref_of_method, type_is_unsafe_function,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
@@ -470,12 +470,11 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span
     if_chain! {
         if !in_external_macro(cx.sess(), item_span);
         if let hir::FnRetTy::Return(ref ty) = decl.output;
-        if let hir::TyKind::Path(ref qpath) = ty.kind;
-        if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym::result_type);
-        if let Some(ref args) = last_path_segment(qpath).args;
-        if let [_, hir::GenericArg::Type(ref err_ty)] = args.args;
-        if let hir::TyKind::Tup(t) = err_ty.kind;
-        if t.is_empty();
+        let ty = hir_ty_to_ty(cx.tcx, ty);
+        if is_type_diagnostic_item(cx, ty, sym::result_type);
+        if let ty::Adt(_, substs) = ty.kind();
+        let err_ty = substs.type_at(1);
+        if err_ty.is_unit();
         then {
             span_lint_and_help(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
new file mode 100644 (file)
index 0000000..c5afdf5
--- /dev/null
@@ -0,0 +1,134 @@
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::Applicability;
+use rustc_hir::{self as hir, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::Symbol;
+
+use if_chain::if_chain;
+
+use crate::utils::{snippet, span_lint_and_sugg};
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for struct constructors where the order of the field init
+    /// shorthand in the constructor is inconsistent with the order in the struct definition.
+    ///
+    /// **Why is this bad?** Since the order of fields in a constructor doesn't affect the
+    /// resulted instance as the below example indicates,
+    ///
+    /// ```rust
+    /// #[derive(Debug, PartialEq, Eq)]
+    /// struct Foo {
+    ///     x: i32,
+    ///     y: i32,
+    /// }
+    /// let x = 1;
+    /// let y = 2;
+    ///
+    /// // This assertion never fails.
+    /// assert_eq!(Foo { x, y }, Foo { y, x });
+    /// ```
+    ///
+    /// inconsistent order means nothing and just decreases readability and consistency.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// struct Foo {
+    ///     x: i32,
+    ///     y: i32,
+    /// }
+    /// let x = 1;
+    /// let y = 2;
+    /// Foo { y, x };
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # struct Foo {
+    /// #     x: i32,
+    /// #     y: i32,
+    /// # }
+    /// # let x = 1;
+    /// # let y = 2;
+    /// Foo { x, y };
+    /// ```
+    pub INCONSISTENT_STRUCT_CONSTRUCTOR,
+    style,
+    "the order of the field init shorthand is inconsistent with the order in the struct definition"
+}
+
+declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]);
+
+impl LateLintPass<'_> for InconsistentStructConstructor {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+        if_chain! {
+            if let ExprKind::Struct(qpath, fields, base) = expr.kind;
+            if let Some(def_id)  = cx.qpath_res(qpath, expr.hir_id).opt_def_id();
+            let ty = cx.tcx.type_of(def_id);
+            if let Some(adt_def) = ty.ty_adt_def();
+            if adt_def.is_struct();
+            if let Some(variant) = adt_def.variants.iter().next();
+            if fields.iter().all(|f| f.is_shorthand);
+            then {
+                let mut def_order_map = FxHashMap::default();
+                for (idx, field) in variant.fields.iter().enumerate() {
+                    def_order_map.insert(field.ident.name, idx);
+                }
+
+                if is_consistent_order(fields, &def_order_map) {
+                    return;
+                }
+
+                let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect();
+                ordered_fields.sort_unstable_by_key(|id| def_order_map[id]);
+
+                let mut fields_snippet = String::new();
+                let (last_ident, idents) = ordered_fields.split_last().unwrap();
+                for ident in idents {
+                    fields_snippet.push_str(&format!("{}, ", ident));
+                }
+                fields_snippet.push_str(&last_ident.to_string());
+
+                let base_snippet = if let Some(base) = base {
+                        format!(", ..{}", snippet(cx, base.span, ".."))
+                    } else {
+                        String::new()
+                    };
+
+                let sugg = format!("{} {{ {}{} }}",
+                    snippet(cx, qpath.span(), ".."),
+                    fields_snippet,
+                    base_snippet,
+                    );
+
+                span_lint_and_sugg(
+                    cx,
+                    INCONSISTENT_STRUCT_CONSTRUCTOR,
+                    expr.span,
+                    "inconsistent struct constructor",
+                    "try",
+                    sugg,
+                    Applicability::MachineApplicable,
+                )
+            }
+        }
+    }
+}
+
+// Check whether the order of the fields in the constructor is consistent with the order in the
+// definition.
+fn is_consistent_order<'tcx>(fields: &'tcx [hir::Field<'tcx>], def_order_map: &FxHashMap<Symbol, usize>) -> bool {
+    let mut cur_idx = usize::MIN;
+    for f in fields {
+        let next_idx = def_order_map[&f.ident.name];
+        if cur_idx > next_idx {
+            return false;
+        }
+        cur_idx = next_idx;
+    }
+
+    true
+}
index 76e7a4992d345c4c57738d2a9636adb3ec582472..a95321ea7e2af39fed82690aa512e82fe7f314c0 100644 (file)
@@ -106,6 +106,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<
             let decl = &signature.decl;
             if decl.implicit_self.has_implicit_self();
             if decl.inputs.len() == 1;
+            if impl_item.generics.params.is_empty();
 
             // Check if return type is String
             if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::string_type);
index d96911fac1a075cc023cbd1408138efbeb85dda1..176eeadcc630a99cf2974c61bf34d1cb0d634e03 100644 (file)
@@ -1,10 +1,7 @@
 // error-pattern:cargo-clippy
 
-#![feature(bindings_after_at)]
 #![feature(box_patterns)]
 #![feature(box_syntax)]
-#![feature(concat_idents)]
-#![feature(crate_visibility_modifier)]
 #![feature(drain_filter)]
 #![feature(in_band_lifetimes)]
 #![feature(once_cell)]
@@ -149,6 +146,20 @@ macro_rules! declare_clippy_lint {
     };
 }
 
+#[macro_export]
+macro_rules! sym {
+    ( $($x:tt)* ) => { clippy_utils::sym!($($x)*) }
+}
+
+#[macro_export]
+macro_rules! unwrap_cargo_metadata {
+    ( $($x:tt)* ) => { clippy_utils::unwrap_cargo_metadata!($($x)*) }
+}
+
+macro_rules! extract_msrv_attr {
+    ( $($x:tt)* ) => { clippy_utils::extract_msrv_attr!($($x)*); }
+}
+
 mod consts;
 #[macro_use]
 mod utils;
@@ -181,6 +192,7 @@ macro_rules! declare_clippy_lint {
 mod create_dir;
 mod dbg_macro;
 mod default;
+mod default_numeric_fallback;
 mod dereference;
 mod derive;
 mod disallowed_method;
@@ -210,6 +222,7 @@ macro_rules! declare_clippy_lint {
 mod format;
 mod formatting;
 mod from_over_into;
+mod from_str_radix_10;
 mod functions;
 mod future_not_send;
 mod get_last_with_len;
@@ -219,6 +232,7 @@ macro_rules! declare_clippy_lint {
 mod if_not_else;
 mod implicit_return;
 mod implicit_saturating_sub;
+mod inconsistent_struct_constructor;
 mod indexing_slicing;
 mod infinite_iter;
 mod inherent_impl;
@@ -239,6 +253,7 @@ macro_rules! declare_clippy_lint {
 mod macro_use;
 mod main_recursion;
 mod manual_async_fn;
+mod manual_map;
 mod manual_non_exhaustive;
 mod manual_ok_or;
 mod manual_strip;
@@ -585,6 +600,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &dbg_macro::DBG_MACRO,
         &default::DEFAULT_TRAIT_ACCESS,
         &default::FIELD_REASSIGN_WITH_DEFAULT,
+        &default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
         &dereference::EXPLICIT_DEREF_METHODS,
         &derive::DERIVE_HASH_XOR_EQ,
         &derive::DERIVE_ORD_XOR_PARTIAL_ORD,
@@ -637,6 +653,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &formatting::SUSPICIOUS_ELSE_FORMATTING,
         &formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
         &from_over_into::FROM_OVER_INTO,
+        &from_str_radix_10::FROM_STR_RADIX_10,
         &functions::DOUBLE_MUST_USE,
         &functions::MUST_USE_CANDIDATE,
         &functions::MUST_USE_UNIT,
@@ -652,6 +669,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &if_not_else::IF_NOT_ELSE,
         &implicit_return::IMPLICIT_RETURN,
         &implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
+        &inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
         &indexing_slicing::INDEXING_SLICING,
         &indexing_slicing::OUT_OF_BOUNDS_INDEXING,
         &infinite_iter::INFINITE_ITER,
@@ -702,6 +720,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &macro_use::MACRO_USE_IMPORTS,
         &main_recursion::MAIN_RECURSION,
         &manual_async_fn::MANUAL_ASYNC_FN,
+        &manual_map::MANUAL_MAP,
         &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
         &manual_ok_or::MANUAL_OK_OR,
         &manual_strip::MANUAL_STRIP,
@@ -1031,6 +1050,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box strings::StringAdd);
     store.register_late_pass(|| box implicit_return::ImplicitReturn);
     store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
+    store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback);
+    store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor);
 
     let msrv = conf.msrv.as_ref().and_then(|s| {
         parse_msrv(s, None, None).or_else(|| {
@@ -1195,7 +1216,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     let enum_variant_name_threshold = conf.enum_variant_name_threshold;
     store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold));
     store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments);
-    store.register_early_pass(|| box upper_case_acronyms::UpperCaseAcronyms);
+    let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
+    store.register_early_pass(move || box upper_case_acronyms::UpperCaseAcronyms::new(upper_case_acronyms_aggressive));
     store.register_late_pass(|| box default::Default::default());
     store.register_late_pass(|| box unused_self::UnusedSelf);
     store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall);
@@ -1256,6 +1278,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || box types::PtrAsPtr::new(msrv));
     store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons);
     store.register_late_pass(|| box redundant_slicing::RedundantSlicing);
+    store.register_late_pass(|| box from_str_radix_10::FromStrRadix10);
+    store.register_late_pass(|| box manual_map::ManualMap);
 
     store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
         LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1265,6 +1289,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
         LintId::of(&create_dir::CREATE_DIR),
         LintId::of(&dbg_macro::DBG_MACRO),
+        LintId::of(&default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
         LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
         LintId::of(&exhaustive_items::EXHAUSTIVE_ENUMS),
         LintId::of(&exhaustive_items::EXHAUSTIVE_STRUCTS),
@@ -1389,6 +1414,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&types::PTR_AS_PTR),
         LintId::of(&unicode::NON_ASCII_LITERAL),
         LintId::of(&unicode::UNICODE_NOT_NFC),
+        LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS),
         LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS),
         LintId::of(&unused_self::UNUSED_SELF),
         LintId::of(&wildcard_imports::ENUM_GLOB_USE),
@@ -1468,6 +1494,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
         LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
         LintId::of(&from_over_into::FROM_OVER_INTO),
+        LintId::of(&from_str_radix_10::FROM_STR_RADIX_10),
         LintId::of(&functions::DOUBLE_MUST_USE),
         LintId::of(&functions::MUST_USE_UNIT),
         LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
@@ -1477,6 +1504,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&identity_op::IDENTITY_OP),
         LintId::of(&if_let_mutex::IF_LET_MUTEX),
         LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
+        LintId::of(&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
         LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING),
         LintId::of(&infinite_iter::INFINITE_ITER),
         LintId::of(&inherent_to_string::INHERENT_TO_STRING),
@@ -1512,6 +1540,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&loops::WHILE_LET_ON_ITERATOR),
         LintId::of(&main_recursion::MAIN_RECURSION),
         LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
+        LintId::of(&manual_map::MANUAL_MAP),
         LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
         LintId::of(&manual_strip::MANUAL_STRIP),
         LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR),
@@ -1682,7 +1711,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
         LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
         LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
-        LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS),
         LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
         LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
         LintId::of(&unused_unit::UNUSED_UNIT),
@@ -1724,10 +1752,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
         LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
         LintId::of(&from_over_into::FROM_OVER_INTO),
+        LintId::of(&from_str_radix_10::FROM_STR_RADIX_10),
         LintId::of(&functions::DOUBLE_MUST_USE),
         LintId::of(&functions::MUST_USE_UNIT),
         LintId::of(&functions::RESULT_UNIT_ERR),
         LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
+        LintId::of(&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
         LintId::of(&inherent_to_string::INHERENT_TO_STRING),
         LintId::of(&len_zero::COMPARISON_TO_EMPTY),
         LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
@@ -1741,6 +1771,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&loops::WHILE_LET_ON_ITERATOR),
         LintId::of(&main_recursion::MAIN_RECURSION),
         LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
+        LintId::of(&manual_map::MANUAL_MAP),
         LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
         LintId::of(&map_clone::MAP_CLONE),
         LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
@@ -1899,7 +1930,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&types::UNNECESSARY_CAST),
         LintId::of(&types::VEC_BOX),
         LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
-        LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS),
         LintId::of(&unwrap::UNNECESSARY_UNWRAP),
         LintId::of(&useless_conversion::USELESS_CONVERSION),
         LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO),
diff --git a/src/tools/clippy/clippy_lints/src/manual_map.rs b/src/tools/clippy/clippy_lints/src/manual_map.rs
new file mode 100644 (file)
index 0000000..a50a394
--- /dev/null
@@ -0,0 +1,274 @@
+use crate::{
+    map_unit_fn::OPTION_MAP_UNIT_FN,
+    matches::MATCH_AS_REF,
+    utils::{
+        is_allowed, is_type_diagnostic_item, match_def_path, match_var, paths, peel_hir_expr_refs,
+        peel_mid_ty_refs_is_mutable, snippet_with_applicability, span_lint_and_sugg,
+    },
+};
+use rustc_ast::util::parser::PREC_POSTFIX;
+use rustc_errors::Applicability;
+use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, QPath};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::{sym, Ident};
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for usages of `match` which could be implemented using `map`
+    ///
+    /// **Why is this bad?** Using the `map` method is clearer and more concise.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// match Some(0) {
+    ///     Some(x) => Some(x + 1),
+    ///     None => None,
+    /// };
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// Some(0).map(|x| x + 1);
+    /// ```
+    pub MANUAL_MAP,
+    style,
+    "reimplementation of `map`"
+}
+
+declare_lint_pass!(ManualMap => [MANUAL_MAP]);
+
+impl LateLintPass<'_> for ManualMap {
+    #[allow(clippy::too_many_lines)]
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if in_external_macro(cx.sess(), expr.span) {
+            return;
+        }
+
+        if let ExprKind::Match(scrutinee, [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], _) =
+            expr.kind
+        {
+            let (scrutinee_ty, ty_ref_count, ty_mutability) =
+                peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee));
+            if !is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type)
+                || !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type)
+            {
+                return;
+            }
+
+            let (some_expr, some_pat, pat_ref_count, is_wild_none) =
+                match (try_parse_pattern(cx, arm1.pat), try_parse_pattern(cx, arm2.pat)) {
+                    (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count }))
+                        if is_none_expr(cx, arm1.body) =>
+                    {
+                        (arm2.body, pattern, ref_count, true)
+                    },
+                    (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count }))
+                        if is_none_expr(cx, arm1.body) =>
+                    {
+                        (arm2.body, pattern, ref_count, false)
+                    },
+                    (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild))
+                        if is_none_expr(cx, arm2.body) =>
+                    {
+                        (arm1.body, pattern, ref_count, true)
+                    },
+                    (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None))
+                        if is_none_expr(cx, arm2.body) =>
+                    {
+                        (arm1.body, pattern, ref_count, false)
+                    },
+                    _ => return,
+                };
+
+            // Top level or patterns aren't allowed in closures.
+            if matches!(some_pat.kind, PatKind::Or(_)) {
+                return;
+            }
+
+            let some_expr = match get_some_expr(cx, some_expr) {
+                Some(expr) => expr,
+                None => return,
+            };
+
+            if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
+                && !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
+            {
+                return;
+            }
+
+            // Determine which binding mode to use.
+            let explicit_ref = some_pat.contains_explicit_ref_binding();
+            let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
+
+            let as_ref_str = match binding_ref {
+                Some(Mutability::Mut) => ".as_mut()",
+                Some(Mutability::Not) => ".as_ref()",
+                None => "",
+            };
+
+            let mut app = Applicability::MachineApplicable;
+
+            // Remove address-of expressions from the scrutinee. `as_ref` will be called,
+            // the type is copyable, or the option is being passed by value.
+            let scrutinee = peel_hir_expr_refs(scrutinee).0;
+            let scrutinee_str = snippet_with_applicability(cx, scrutinee.span, "_", &mut app);
+            let scrutinee_str = if expr.precedence().order() < PREC_POSTFIX {
+                // Parens are needed to chain method calls.
+                format!("({})", scrutinee_str)
+            } else {
+                scrutinee_str.into()
+            };
+
+            let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind {
+                if let Some(func) = can_pass_as_func(cx, some_binding, some_expr) {
+                    snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
+                } else {
+                    if match_var(some_expr, some_binding.name)
+                        && !is_allowed(cx, MATCH_AS_REF, expr.hir_id)
+                        && binding_ref.is_some()
+                    {
+                        return;
+                    }
+
+                    // `ref` and `ref mut` annotations were handled earlier.
+                    let annotation = if matches!(annotation, BindingAnnotation::Mutable) {
+                        "mut "
+                    } else {
+                        ""
+                    };
+                    format!(
+                        "|{}{}| {}",
+                        annotation,
+                        some_binding,
+                        snippet_with_applicability(cx, some_expr.span, "..", &mut app)
+                    )
+                }
+            } else if !is_wild_none && explicit_ref.is_none() {
+                // TODO: handle explicit reference annotations.
+                format!(
+                    "|{}| {}",
+                    snippet_with_applicability(cx, some_pat.span, "..", &mut app),
+                    snippet_with_applicability(cx, some_expr.span, "..", &mut app)
+                )
+            } else {
+                // Refutable bindings and mixed reference annotations can't be handled by `map`.
+                return;
+            };
+
+            span_lint_and_sugg(
+                cx,
+                MANUAL_MAP,
+                expr.span,
+                "manual implementation of `Option::map`",
+                "try this",
+                format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str),
+                app,
+            );
+        }
+    }
+}
+
+// Checks whether the expression could be passed as a function, or whether a closure is needed.
+// Returns the function to be passed to `map` if it exists.
+fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    match expr.kind {
+        ExprKind::Call(func, [arg])
+            if match_var(arg, binding.name) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
+        {
+            Some(func)
+        },
+        _ => None,
+    }
+}
+
+enum OptionPat<'a> {
+    Wild,
+    None,
+    Some {
+        // The pattern contained in the `Some` tuple.
+        pattern: &'a Pat<'a>,
+        // The number of references before the `Some` tuple.
+        // e.g. `&&Some(_)` has a ref count of 2.
+        ref_count: usize,
+    },
+}
+
+// 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<'_>) -> Option<OptionPat<'tcx>> {
+    fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize) -> Option<OptionPat<'tcx>> {
+        match pat.kind {
+            PatKind::Wild => Some(OptionPat::Wild),
+            PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1),
+            PatKind::Path(QPath::Resolved(None, path))
+                if path
+                    .res
+                    .opt_def_id()
+                    .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)) =>
+            {
+                Some(OptionPat::None)
+            },
+            PatKind::TupleStruct(QPath::Resolved(None, path), [pattern], _)
+                if path
+                    .res
+                    .opt_def_id()
+                    .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_SOME)) =>
+            {
+                Some(OptionPat::Some { pattern, ref_count })
+            },
+            _ => None,
+        }
+    }
+    f(cx, pat, 0)
+}
+
+// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
+fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    // TODO: Allow more complex expressions.
+    match expr.kind {
+        ExprKind::Call(
+            Expr {
+                kind: ExprKind::Path(QPath::Resolved(None, path)),
+                ..
+            },
+            [arg],
+        ) => {
+            if match_def_path(cx, path.res.opt_def_id()?, &paths::OPTION_SOME) {
+                Some(arg)
+            } else {
+                None
+            }
+        },
+        ExprKind::Block(
+            Block {
+                stmts: [],
+                expr: Some(expr),
+                ..
+            },
+            _,
+        ) => get_some_expr(cx, expr),
+        _ => None,
+    }
+}
+
+// Checks for the `None` value.
+fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+    match expr.kind {
+        ExprKind::Path(QPath::Resolved(None, path)) => path
+            .res
+            .opt_def_id()
+            .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)),
+        ExprKind::Block(
+            Block {
+                stmts: [],
+                expr: Some(expr),
+                ..
+            },
+            _,
+        ) => is_none_expr(cx, expr),
+        _ => false,
+    }
+}
index e33001b16bcde20b2a172738dcf71d98628d1f1f..efc8b13942507dc408df040a4d281aae391d7268 100644 (file)
@@ -3,19 +3,19 @@
 use crate::utils::visitors::LocalUsedVisitor;
 use crate::utils::{
     expr_block, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
-    is_type_diagnostic_item, is_wild, match_qpath, match_type, meets_msrv, multispan_sugg, path_to_local_id,
-    peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block, snippet_opt,
-    snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
-    strip_pat_refs,
+    is_type_diagnostic_item, is_wild, match_qpath, match_type, meets_msrv, multispan_sugg, path_to_local,
+    path_to_local_id, peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block,
+    snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
+    span_lint_and_then, strip_pat_refs,
 };
 use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::Applicability;
 use rustc_hir::def::CtorKind;
 use rustc_hir::{
-    Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, Local, MatchSource, Mutability, Node, Pat,
+    Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, Mutability, Node, Pat,
     PatKind, QPath, RangeEnd,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -24,7 +24,7 @@
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::{Span, Spanned};
-use rustc_span::{sym, Symbol};
+use rustc_span::sym;
 use std::cmp::Ordering;
 use std::collections::hash_map::Entry;
 use std::collections::Bound;
@@ -1873,13 +1873,6 @@ fn test_overlapping() {
 
 /// Implementation of `MATCH_SAME_ARMS`.
 fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
-    fn same_bindings<'tcx>(lhs: &FxHashMap<Symbol, Ty<'tcx>>, rhs: &FxHashMap<Symbol, Ty<'tcx>>) -> bool {
-        lhs.len() == rhs.len()
-            && lhs
-                .iter()
-                .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty)))
-    }
-
     if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind {
         let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
             let mut h = SpanlessHash::new(cx);
@@ -1891,12 +1884,38 @@ fn same_bindings<'tcx>(lhs: &FxHashMap<Symbol, Ty<'tcx>>, rhs: &FxHashMap<Symbol
             let min_index = usize::min(lindex, rindex);
             let max_index = usize::max(lindex, rindex);
 
+            let mut local_map: FxHashMap<HirId, HirId> = FxHashMap::default();
+            let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
+                if_chain! {
+                    if let Some(a_id) = path_to_local(a);
+                    if let Some(b_id) = path_to_local(b);
+                    let entry = match local_map.entry(a_id) {
+                        Entry::Vacant(entry) => entry,
+                        // check if using the same bindings as before
+                        Entry::Occupied(entry) => return *entry.get() == b_id,
+                    };
+                    // the names technically don't have to match; this makes the lint more conservative
+                    if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
+                    if TyS::same_type(cx.typeck_results().expr_ty(a), cx.typeck_results().expr_ty(b));
+                    if pat_contains_local(lhs.pat, a_id);
+                    if pat_contains_local(rhs.pat, b_id);
+                    then {
+                        entry.insert(b_id);
+                        true
+                    } else {
+                        false
+                    }
+                }
+            };
             // Arms with a guard are ignored, those can’t always be merged together
             // This is also the case for arms in-between each there is an arm with a guard
-            (min_index..=max_index).all(|index| arms[index].guard.is_none()) &&
-                SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) &&
-                // all patterns should have the same bindings
-                same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat))
+            (min_index..=max_index).all(|index| arms[index].guard.is_none())
+                && SpanlessEq::new(cx)
+                    .expr_fallback(eq_fallback)
+                    .eq_expr(&lhs.body, &rhs.body)
+                // these checks could be removed to allow unused bindings
+                && bindings_eq(lhs.pat, local_map.keys().copied().collect())
+                && bindings_eq(rhs.pat, local_map.values().copied().collect())
         };
 
         let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
@@ -1939,50 +1958,18 @@ fn same_bindings<'tcx>(lhs: &FxHashMap<Symbol, Ty<'tcx>>, rhs: &FxHashMap<Symbol
     }
 }
 
-/// Returns the list of bindings in a pattern.
-fn bindings<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> FxHashMap<Symbol, Ty<'tcx>> {
-    fn bindings_impl<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, map: &mut FxHashMap<Symbol, Ty<'tcx>>) {
-        match pat.kind {
-            PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map),
-            PatKind::TupleStruct(_, pats, _) => {
-                for pat in pats {
-                    bindings_impl(cx, pat, map);
-                }
-            },
-            PatKind::Binding(.., ident, ref as_pat) => {
-                if let Entry::Vacant(v) = map.entry(ident.name) {
-                    v.insert(cx.typeck_results().pat_ty(pat));
-                }
-                if let Some(ref as_pat) = *as_pat {
-                    bindings_impl(cx, as_pat, map);
-                }
-            },
-            PatKind::Or(fields) | PatKind::Tuple(fields, _) => {
-                for pat in fields {
-                    bindings_impl(cx, pat, map);
-                }
-            },
-            PatKind::Struct(_, fields, _) => {
-                for pat in fields {
-                    bindings_impl(cx, &pat.pat, map);
-                }
-            },
-            PatKind::Slice(lhs, ref mid, rhs) => {
-                for pat in lhs {
-                    bindings_impl(cx, pat, map);
-                }
-                if let Some(ref mid) = *mid {
-                    bindings_impl(cx, mid, map);
-                }
-                for pat in rhs {
-                    bindings_impl(cx, pat, map);
-                }
-            },
-            PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (),
-        }
-    }
-
-    let mut result = FxHashMap::default();
-    bindings_impl(cx, pat, &mut result);
+fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool {
+    let mut result = false;
+    pat.walk_short(|p| {
+        result |= matches!(p.kind, PatKind::Binding(_, binding_id, ..) if binding_id == id);
+        !result
+    });
     result
 }
+
+/// Returns true if all the bindings in the `Pat` are in `ids` and vice versa
+fn bindings_eq(pat: &Pat<'_>, mut ids: FxHashSet<HirId>) -> bool {
+    let mut result = true;
+    pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id));
+    result && ids.is_empty()
+}
index 9b45d38afd42f5eeed1a3064e2fbefd2845dd995..00a707107bce91f8404057d367c1fe74d757879d 100644 (file)
@@ -212,10 +212,10 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
             if !expr_borrows(cx, left_expr) {
                 return Some(LintTrigger::SortByKey(SortByKeyDetection {
                     vec_name,
-                    unstable,
                     closure_arg,
                     closure_body,
-                    reverse
+                    reverse,
+                    unstable,
                 }));
             }
         }
index 8ac5dd696b7620d85216c281603c286582ed5411..1e58576d0599cdeced4ddd82326ebad6f93cf0a8 100644 (file)
@@ -1,5 +1,5 @@
 use crate::utils::{
-    contains_return, in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then,
+    contains_return, in_macro, match_qpath, paths, return_ty, snippet, span_lint_and_then,
     visitors::find_all_ret_expressions,
 };
 use if_chain::if_chain;
@@ -7,7 +7,7 @@
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
@@ -17,8 +17,8 @@
     ///
     /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned.
     ///
-    /// **Known problems:** Since this lint changes function type signature, you may need to
-    /// adjust some code at callee side.
+    /// **Known problems:** There can be false positives if the function signature is designed to
+    /// fit some external requirement.
     ///
     /// **Example:**
     ///
@@ -48,7 +48,7 @@
     /// }
     /// ```
     pub UNNECESSARY_WRAPS,
-    complexity,
+    pedantic,
     "functions that only return `Ok` or `Some`"
 }
 
@@ -64,6 +64,7 @@ fn check_fn(
         span: Span,
         hir_id: HirId,
     ) {
+        // Abort if public function/method or closure.
         match fn_kind {
             FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => {
                 if visibility.node.is_pub() {
@@ -74,6 +75,7 @@ fn check_fn(
             _ => (),
         }
 
+        // Abort if the method is implementing a trait or of it a trait method.
         if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
             if matches!(
                 item.kind,
@@ -83,25 +85,44 @@ fn check_fn(
             }
         }
 
-        let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::option_type) {
-            ("Option", &paths::OPTION_SOME)
-        } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) {
-            ("Result", &paths::RESULT_OK)
+        // Get the wrapper and inner types, if can't, abort.
+        let (return_type_label, path, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() {
+            if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did) {
+                ("Option", &paths::OPTION_SOME, subst.type_at(0))
+            } else if cx.tcx.is_diagnostic_item(sym::result_type, adt_def.did) {
+                ("Result", &paths::RESULT_OK, subst.type_at(0))
+            } else {
+                return;
+            }
         } else {
             return;
         };
 
+        // Check if all return expression respect the following condition and collect them.
         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);
+                // Check if a function call.
                 if let ExprKind::Call(ref func, ref args) = ret_expr.kind;
+                // Get the Path of the function call.
                 if let ExprKind::Path(ref qpath) = func.kind;
+                // Check if OPTION_SOME or RESULT_OK, depending on return type.
                 if match_qpath(qpath, path);
                 if args.len() == 1;
+                // Make sure the function argument does not contain a return expression.
                 if !contains_return(&args[0]);
                 then {
-                    suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string()));
+                    suggs.push(
+                        (
+                            ret_expr.span,
+                            if inner_type.is_unit() {
+                                "".to_string()
+                            } else {
+                                snippet(cx, args[0].span.source_callsite(), "..").to_string()
+                            }
+                        )
+                    );
                     true
                 } else {
                     false
@@ -110,39 +131,34 @@ fn check_fn(
         });
 
         if can_sugg && !suggs.is_empty() {
-            span_lint_and_then(
-                cx,
-                UNNECESSARY_WRAPS,
-                span,
-                format!(
-                    "this function's return value is unnecessarily wrapped by `{}`",
-                    return_type
+            let (lint_msg, return_type_sugg_msg, return_type_sugg, body_sugg_msg) = if inner_type.is_unit() {
+                (
+                    "this function's return value is unnecessary".to_string(),
+                    "remove the return type...".to_string(),
+                    snippet(cx, fn_decl.output.span(), "..").to_string(),
+                    "...and then remove returned values",
                 )
-                .as_str(),
-                |diag| {
-                    let inner_ty = return_ty(cx, hir_id)
-                        .walk()
-                        .skip(1) // skip `std::option::Option` or `std::result::Result`
-                        .take(1) // take the first outermost inner type
-                        .filter_map(|inner| match inner.unpack() {
-                            GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()),
-                            _ => None,
-                        });
-                    inner_ty.for_each(|inner_ty| {
-                        diag.span_suggestion(
-                            fn_decl.output.span(),
-                            format!("remove `{}` from the return type...", return_type).as_str(),
-                            inner_ty,
-                            Applicability::MaybeIncorrect,
-                        );
-                    });
-                    diag.multipart_suggestion(
-                        "...and change the returning expressions",
-                        suggs,
-                        Applicability::MaybeIncorrect,
-                    );
-                },
-            );
+            } else {
+                (
+                    format!(
+                        "this function's return value is unnecessarily wrapped by `{}`",
+                        return_type_label
+                    ),
+                    format!("remove `{}` from the return type...", return_type_label),
+                    inner_type.to_string(),
+                    "...and then change returning expressions",
+                )
+            };
+
+            span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg.as_str(), |diag| {
+                diag.span_suggestion(
+                    fn_decl.output.span(),
+                    return_type_sugg_msg.as_str(),
+                    return_type_sugg,
+                    Applicability::MaybeIncorrect,
+                );
+                diag.multipart_suggestion(body_sugg_msg, suggs, Applicability::MaybeIncorrect);
+            });
         }
     }
 }
index 61e7031716a9d8098f1fd9288700c699b75d3309..0470e1dbbb8122ae9804b2634f05986e9458a372 100644 (file)
@@ -5,16 +5,20 @@
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::Ident;
 
 declare_clippy_lint! {
-    /// **What it does:** Checks for camel case name containing a capitalized acronym.
+    /// **What it does:** Checks for fully capitalized names and optionally names containing a capitalized acronym.
     ///
     /// **Why is this bad?** In CamelCase, acronyms count as one word.
     /// See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case)
     /// for more.
     ///
+    /// By default, the lint only triggers on fully-capitalized names.
+    /// You can use the `upper-case-acronyms-aggressive: true` config option to enable linting
+    /// on all camel case names
+    ///
     /// **Known problems:** When two acronyms are contiguous, the lint can't tell where
     /// the first acronym ends and the second starts, so it suggests to lowercase all of
     /// the letters in the second acronym.
     "capitalized acronyms are against the naming convention"
 }
 
-declare_lint_pass!(UpperCaseAcronyms => [UPPER_CASE_ACRONYMS]);
+#[derive(Default)]
+pub struct UpperCaseAcronyms {
+    upper_case_acronyms_aggressive: bool,
+}
+
+impl UpperCaseAcronyms {
+    pub fn new(aggressive: bool) -> Self {
+        Self {
+            upper_case_acronyms_aggressive: aggressive,
+        }
+    }
+}
+
+impl_lint_pass!(UpperCaseAcronyms => [UPPER_CASE_ACRONYMS]);
 
 fn correct_ident(ident: &str) -> String {
     let ident = ident.chars().rev().collect::<String>();
@@ -56,11 +73,18 @@ fn correct_ident(ident: &str) -> String {
     ident
 }
 
-fn check_ident(cx: &EarlyContext<'_>, ident: &Ident) {
+fn check_ident(cx: &EarlyContext<'_>, ident: &Ident, be_aggressive: bool) {
     let span = ident.span;
     let ident = &ident.as_str();
     let corrected = correct_ident(ident);
-    if ident != &corrected {
+    // warn if we have pure-uppercase idents
+    // assume that two-letter words are some kind of valid abbreviation like FP for false positive
+    // (and don't warn)
+    if (ident.chars().all(|c| c.is_ascii_uppercase()) && ident.len() > 2)
+    // otherwise, warn if we have SOmeTHING lIKE THIs but only warn with the aggressive 
+    // upper-case-acronyms-aggressive config option enabled
+    || (be_aggressive && ident != &corrected)
+    {
         span_lint_and_sugg(
             cx,
             UPPER_CASE_ACRONYMS,
@@ -82,12 +106,12 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, it: &Item) {
                 ItemKind::TyAlias(..) | ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Trait(..)
             );
             then {
-                check_ident(cx, &it.ident);
+                check_ident(cx, &it.ident, self.upper_case_acronyms_aggressive);
             }
         }
     }
 
     fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &Variant) {
-        check_ident(cx, &v.ident);
+        check_ident(cx, &v.ident, self.upper_case_acronyms_aggressive);
     }
 }
index f2ff8c959c2e6405691664bd6c32662d81cfe74e..be7b9e9ff2dcc20186e58ec6409a3ce160987073 100644 (file)
@@ -1,24 +1,24 @@
+use crate::utils::{in_macro, meets_msrv, snippet_opt, span_lint_and_sugg};
 use if_chain::if_chain;
+
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::def::{DefKind, Res};
-use rustc_hir::intravisit::{walk_item, walk_path, walk_ty, NestedVisitorMap, Visitor};
+use rustc_hir::def::DefKind;
 use rustc_hir::{
-    def, FnDecl, FnRetTy, FnSig, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Path, PathSegment, QPath,
-    TyKind,
+    def,
+    def_id::LocalDefId,
+    intravisit::{walk_ty, NestedVisitorMap, Visitor},
+    Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, PathSegment,
+    QPath, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::map::Map;
-use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty;
-use rustc_middle::ty::{DefIdTree, Ty};
+use rustc_middle::ty::{AssocKind, Ty, TyS};
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::symbol::kw;
+use rustc_span::{BytePos, Span};
 use rustc_typeck::hir_ty_to_ty;
 
-use crate::utils::{differing_macro_contexts, meets_msrv, span_lint_and_sugg};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for unnecessary repetition of structure name when a
     /// replacement with `Self` is applicable.
     /// feels inconsistent.
     ///
     /// **Known problems:**
-    /// - False positive when using associated types ([#2843](https://github.com/rust-lang/rust-clippy/issues/2843))
-    /// - False positives in some situations when using generics ([#3410](https://github.com/rust-lang/rust-clippy/issues/3410))
+    /// - Unaddressed false negative in fn bodies of trait implementations
+    /// - False positive with assotiated types in traits (#4140)
     ///
     /// **Example:**
+    ///
     /// ```rust
     /// struct Foo {}
     /// impl Foo {
     "unnecessary structure name repetition whereas `Self` is applicable"
 }
 
-impl_lint_pass!(UseSelf => [USE_SELF]);
-
-const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
-
-fn span_use_self_lint(cx: &LateContext<'_>, path: &Path<'_>, last_segment: Option<&PathSegment<'_>>) {
-    let last_segment = last_segment.unwrap_or_else(|| path.segments.last().expect(SEGMENTS_MSG));
+#[derive(Default)]
+pub struct UseSelf {
+    msrv: Option<RustcVersion>,
+    stack: Vec<StackItem>,
+}
 
-    // Path segments only include actual path, no methods or fields.
-    let last_path_span = last_segment.ident.span;
+const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
 
-    if differing_macro_contexts(path.span, last_path_span) {
-        return;
+impl UseSelf {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self {
+            msrv,
+            ..Self::default()
+        }
     }
-
-    // Only take path up to the end of last_path_span.
-    let span = path.span.with_hi(last_path_span.hi());
-
-    span_lint_and_sugg(
-        cx,
-        USE_SELF,
-        span,
-        "unnecessary structure name repetition",
-        "use the applicable keyword",
-        "Self".to_owned(),
-        Applicability::MachineApplicable,
-    );
 }
 
-// FIXME: always use this (more correct) visitor, not just in method signatures.
-struct SemanticUseSelfVisitor<'a, 'tcx> {
-    cx: &'a LateContext<'tcx>,
-    self_ty: Ty<'tcx>,
+#[derive(Debug)]
+enum StackItem {
+    Check {
+        hir_id: HirId,
+        impl_trait_ref_def_id: Option<LocalDefId>,
+        types_to_skip: Vec<HirId>,
+        types_to_lint: Vec<HirId>,
+    },
+    NoCheck,
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for SemanticUseSelfVisitor<'a, 'tcx> {
-    type Map = Map<'tcx>;
+impl_lint_pass!(UseSelf => [USE_SELF]);
 
-    fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) {
-        if let TyKind::Path(QPath::Resolved(_, path)) = &hir_ty.kind {
-            match path.res {
-                def::Res::SelfTy(..) => {},
-                _ => {
-                    if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty {
-                        span_use_self_lint(self.cx, path, None);
-                    }
-                },
-            }
-        }
+const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
 
-        walk_ty(self, hir_ty)
+impl<'tcx> LateLintPass<'tcx> for UseSelf {
+    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+        // We push the self types of `impl`s on a stack here. Only the top type on the stack is
+        // relevant for linting, since this is the self type of the `impl` we're currently in. To
+        // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
+        // we're in an `impl` or nested item, that we don't want to lint
+        //
+        // NB: If you push something on the stack in this method, remember to also pop it in the
+        // `check_item_post` method.
+        match &item.kind {
+            ItemKind::Impl(Impl {
+                self_ty: hir_self_ty,
+                of_trait,
+                ..
+            }) => {
+                let should_check = if let TyKind::Path(QPath::Resolved(_, ref item_path)) = hir_self_ty.kind {
+                    let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
+                    parameters.as_ref().map_or(true, |params| {
+                        !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
+                    })
+                } else {
+                    false
+                };
+                let impl_trait_ref_def_id = of_trait.as_ref().map(|_| cx.tcx.hir().local_def_id(item.hir_id()));
+                if should_check {
+                    self.stack.push(StackItem::Check {
+                        hir_id: hir_self_ty.hir_id,
+                        impl_trait_ref_def_id,
+                        types_to_lint: Vec::new(),
+                        types_to_skip: Vec::new(),
+                    });
+                } else {
+                    self.stack.push(StackItem::NoCheck);
+                }
+            },
+            ItemKind::Static(..)
+            | ItemKind::Const(..)
+            | ItemKind::Fn(..)
+            | ItemKind::Enum(..)
+            | ItemKind::Struct(..)
+            | ItemKind::Union(..)
+            | ItemKind::Trait(..) => {
+                self.stack.push(StackItem::NoCheck);
+            },
+            _ => (),
+        }
     }
 
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
+    fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
+        use ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
+        match item.kind {
+            Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..) => {
+                self.stack.pop();
+            },
+            _ => (),
+        }
     }
-}
 
-fn check_trait_method_impl_decl<'tcx>(
-    cx: &LateContext<'tcx>,
-    impl_item: &ImplItem<'_>,
-    impl_decl: &'tcx FnDecl<'_>,
-    impl_trait_ref: ty::TraitRef<'tcx>,
-) {
-    let trait_method = cx
-        .tcx
-        .associated_items(impl_trait_ref.def_id)
-        .find_by_name_and_kind(cx.tcx, impl_item.ident, ty::AssocKind::Fn, impl_trait_ref.def_id)
-        .expect("impl method matches a trait method");
-
-    let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id);
-    let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig);
-
-    let output_hir_ty = if let FnRetTy::Return(ty) = &impl_decl.output {
-        Some(&**ty)
-    } else {
-        None
-    };
-
-    // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
-    // `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait.
-    // We use `impl_hir_ty` to see if the type was written as `Self`,
-    // `hir_ty_to_ty(...)` to check semantic types of paths, and
-    // `trait_ty` to determine which parts of the signature in the trait, mention
-    // the type being implemented verbatim (as opposed to `Self`).
-    for (impl_hir_ty, trait_ty) in impl_decl
-        .inputs
-        .iter()
-        .chain(output_hir_ty)
-        .zip(trait_method_sig.inputs_and_output)
-    {
-        // Check if the input/output type in the trait method specifies the implemented
-        // type verbatim, and only suggest `Self` if that isn't the case.
-        // This avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`,
-        // in an `impl Trait for u8`, when the trait always uses `Vec<u8>`.
-        // See also https://github.com/rust-lang/rust-clippy/issues/2894.
-        let self_ty = impl_trait_ref.self_ty();
-        if !trait_ty.walk().any(|inner| inner == self_ty.into()) {
-            let mut visitor = SemanticUseSelfVisitor { cx, self_ty };
-
-            visitor.visit_ty(&impl_hir_ty);
+    fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
+        // We want to skip types in trait `impl`s that aren't declared as `Self` in the trait
+        // declaration. The collection of those types is all this method implementation does.
+        if_chain! {
+            if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind;
+            if let Some(&mut StackItem::Check {
+                impl_trait_ref_def_id: Some(def_id),
+                ref mut types_to_skip,
+                ..
+            }) = self.stack.last_mut();
+            if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(def_id);
+            then {
+                // `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
+                // `Self`.
+                let self_ty = impl_trait_ref.self_ty();
+
+                // `trait_method_sig` is the signature of the function, how it is declared in the
+                // trait, not in the impl of the trait.
+                let trait_method = cx
+                    .tcx
+                    .associated_items(impl_trait_ref.def_id)
+                    .find_by_name_and_kind(cx.tcx, impl_item.ident, AssocKind::Fn, impl_trait_ref.def_id)
+                    .expect("impl method matches a trait method");
+                let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id);
+                let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig);
+
+                // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the
+                // implementation of the trait.
+                let output_hir_ty = if let FnRetTy::Return(ty) = &decl.output {
+                    Some(&**ty)
+                } else {
+                    None
+                };
+                let impl_inputs_outputs = decl.inputs.iter().chain(output_hir_ty);
+
+                // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
+                //
+                // `trait_sem_ty` (of type `ty::Ty`) is the semantic type for the signature in the
+                // trait declaration. This is used to check if `Self` was used in the trait
+                // declaration.
+                //
+                // If `any`where in the `trait_sem_ty` the `self_ty` was used verbatim (as opposed
+                // to `Self`), we want to skip linting that type and all subtypes of it. This
+                // avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`, in an `impl Trait
+                // for u8`, when the trait always uses `Vec<u8>`.
+                //
+                // See also https://github.com/rust-lang/rust-clippy/issues/2894.
+                for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) {
+                    if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) {
+                        let mut visitor = SkipTyCollector::default();
+                        visitor.visit_ty(&impl_hir_ty);
+                        types_to_skip.extend(visitor.types_to_skip);
+                    }
+                }
+            }
         }
     }
-}
 
-const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
+    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) {
+        // `hir_ty_to_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies
+        // we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`.
+        // However the `node_type()` method can *only* be called in bodies.
+        //
+        // This method implementation determines which types should get linted in a `Body` and
+        // which shouldn't, with a visitor. We could directly lint in the visitor, but then we
+        // could only allow this lint on item scope. And we would have to check if those types are
+        // already dealt with in `check_ty` anyway.
+        if let Some(StackItem::Check {
+            hir_id,
+            types_to_lint,
+            types_to_skip,
+            ..
+        }) = self.stack.last_mut()
+        {
+            let self_ty = ty_from_hir_id(cx, *hir_id);
+
+            let mut visitor = LintTyCollector {
+                cx,
+                self_ty,
+                types_to_lint: vec![],
+                types_to_skip: vec![],
+            };
+            visitor.visit_expr(&body.value);
+            types_to_lint.extend(visitor.types_to_lint);
+            types_to_skip.extend(visitor.types_to_skip);
+        }
+    }
 
-pub struct UseSelf {
-    msrv: Option<RustcVersion>,
-}
+    fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
+        if in_macro(hir_ty.span) | in_impl(cx, hir_ty) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
+            return;
+        }
 
-impl UseSelf {
-    #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
-        Self { msrv }
+        let lint_dependend_on_expr_kind = if let Some(StackItem::Check {
+            hir_id,
+            types_to_lint,
+            types_to_skip,
+            ..
+        }) = self.stack.last()
+        {
+            if types_to_skip.contains(&hir_ty.hir_id) {
+                false
+            } else if types_to_lint.contains(&hir_ty.hir_id) {
+                true
+            } else {
+                let self_ty = ty_from_hir_id(cx, *hir_id);
+                should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty)
+            }
+        } else {
+            false
+        };
+
+        if lint_dependend_on_expr_kind {
+            // FIXME: this span manipulation should not be necessary
+            // @flip1995 found an ast lowering issue in
+            // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#l142-l162
+            match cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_ty.hir_id)) {
+                Some(Node::Expr(Expr {
+                    kind: ExprKind::Path(QPath::TypeRelative(_, segment)),
+                    ..
+                })) => span_lint_until_last_segment(cx, hir_ty.span, segment),
+                _ => span_lint(cx, hir_ty.span),
+            }
+        }
     }
-}
 
-impl<'tcx> LateLintPass<'tcx> for UseSelf {
-    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
-            return;
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        fn expr_ty_matches(cx: &LateContext<'_>, expr: &Expr<'_>, self_ty: Ty<'_>) -> bool {
+            let def_id = expr.hir_id.owner;
+            if cx.tcx.has_typeck_results(def_id) {
+                cx.tcx.typeck(def_id).expr_ty_opt(expr) == Some(self_ty)
+            } else {
+                false
+            }
         }
 
-        if in_external_macro(cx.sess(), item.span) {
+        if in_macro(expr.span) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
             return;
         }
-        if_chain! {
-            if let ItemKind::Impl(impl_) = &item.kind;
-            if let TyKind::Path(QPath::Resolved(_, ref item_path)) = impl_.self_ty.kind;
-            then {
-                let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
-                let should_check = parameters.as_ref().map_or(
-                    true,
-                    |params| !params.parenthesized
-                        &&!params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
-                );
 
-                if should_check {
-                    let visitor = &mut UseSelfVisitor {
-                        item_path,
-                        cx,
-                    };
-                    let impl_trait_ref = cx.tcx.impl_trait_ref(item.def_id);
-
-                    if let Some(impl_trait_ref) = impl_trait_ref {
-                        for impl_item_ref in impl_.items {
-                            let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
-                            if let ImplItemKind::Fn(FnSig{ decl: impl_decl, .. }, impl_body_id)
-                                    = &impl_item.kind {
-                                check_trait_method_impl_decl(cx, impl_item, impl_decl, impl_trait_ref);
-
-                                let body = cx.tcx.hir().body(*impl_body_id);
-                                visitor.visit_body(body);
-                            } else {
-                                visitor.visit_impl_item(impl_item);
-                            }
+        if let Some(StackItem::Check { hir_id, .. }) = self.stack.last() {
+            let self_ty = ty_from_hir_id(cx, *hir_id);
+
+            match &expr.kind {
+                ExprKind::Struct(QPath::Resolved(_, path), ..) => {
+                    if expr_ty_matches(cx, expr, self_ty) {
+                        match path.res {
+                            def::Res::SelfTy(..) => (),
+                            def::Res::Def(DefKind::Variant, _) => span_lint_on_path_until_last_segment(cx, path),
+                            _ => {
+                                span_lint(cx, path.span);
+                            },
                         }
-                    } else {
-                        for impl_item_ref in impl_.items {
-                            let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
-                            visitor.visit_impl_item(impl_item);
+                    }
+                },
+                // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
+                ExprKind::Call(fun, _) => {
+                    if let Expr {
+                        kind: ExprKind::Path(ref qpath),
+                        ..
+                    } = fun
+                    {
+                        if expr_ty_matches(cx, expr, self_ty) {
+                            let res = cx.qpath_res(qpath, fun.hir_id);
+
+                            if let def::Res::Def(DefKind::Ctor(ctor_of, _), ..) = res {
+                                match ctor_of {
+                                    def::CtorOf::Variant => {
+                                        span_lint_on_qpath_resolved(cx, qpath, true);
+                                    },
+                                    def::CtorOf::Struct => {
+                                        span_lint_on_qpath_resolved(cx, qpath, false);
+                                    },
+                                }
+                            }
                         }
                     }
-                }
+                },
+                // unit enum variants (`Enum::A`)
+                ExprKind::Path(qpath) => {
+                    if expr_ty_matches(cx, expr, self_ty) {
+                        span_lint_on_qpath_resolved(cx, &qpath, true);
+                    }
+                },
+                _ => (),
             }
         }
     }
+
     extract_msrv_attr!(LateContext);
 }
 
-struct UseSelfVisitor<'a, 'tcx> {
-    item_path: &'a Path<'a>,
+#[derive(Default)]
+struct SkipTyCollector {
+    types_to_skip: Vec<HirId>,
+}
+
+impl<'tcx> Visitor<'tcx> for SkipTyCollector {
+    type Map = Map<'tcx>;
+
+    fn visit_ty(&mut self, hir_ty: &hir::Ty<'_>) {
+        self.types_to_skip.push(hir_ty.hir_id);
+
+        walk_ty(self, hir_ty)
+    }
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+}
+
+struct LintTyCollector<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
+    self_ty: Ty<'tcx>,
+    types_to_lint: Vec<HirId>,
+    types_to_skip: Vec<HirId>,
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for UseSelfVisitor<'a, 'tcx> {
+impl<'a, 'tcx> Visitor<'tcx> for LintTyCollector<'a, 'tcx> {
     type Map = Map<'tcx>;
 
-    fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
-        if !path.segments.iter().any(|p| p.ident.span.is_dummy()) {
-            if path.segments.len() >= 2 {
-                let last_but_one = &path.segments[path.segments.len() - 2];
-                if last_but_one.ident.name != kw::SelfUpper {
-                    let enum_def_id = match path.res {
-                        Res::Def(DefKind::Variant, variant_def_id) => self.cx.tcx.parent(variant_def_id),
-                        Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), ctor_def_id) => {
-                            let variant_def_id = self.cx.tcx.parent(ctor_def_id);
-                            variant_def_id.and_then(|def_id| self.cx.tcx.parent(def_id))
-                        },
-                        _ => None,
-                    };
-
-                    if self.item_path.res.opt_def_id() == enum_def_id {
-                        span_use_self_lint(self.cx, path, Some(last_but_one));
-                    }
-                }
+    fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) {
+        if_chain! {
+            if let Some(ty) = self.cx.typeck_results().node_type_opt(hir_ty.hir_id);
+            if should_lint_ty(hir_ty, ty, self.self_ty);
+            then {
+                self.types_to_lint.push(hir_ty.hir_id);
+            } else {
+                self.types_to_skip.push(hir_ty.hir_id);
             }
+        }
 
-            if path.segments.last().expect(SEGMENTS_MSG).ident.name != kw::SelfUpper {
-                if self.item_path.res == path.res {
-                    span_use_self_lint(self.cx, path, None);
-                } else if let Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), ctor_def_id) = path.res {
-                    if self.item_path.res.opt_def_id() == self.cx.tcx.parent(ctor_def_id) {
-                        span_use_self_lint(self.cx, path, None);
-                    }
-                }
-            }
+        walk_ty(self, hir_ty)
+    }
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+}
+
+fn span_lint(cx: &LateContext<'_>, span: Span) {
+    span_lint_and_sugg(
+        cx,
+        USE_SELF,
+        span,
+        "unnecessary structure name repetition",
+        "use the applicable keyword",
+        "Self".to_owned(),
+        Applicability::MachineApplicable,
+    );
+}
+
+#[allow(clippy::cast_possible_truncation)]
+fn span_lint_until_last_segment(cx: &LateContext<'_>, span: Span, segment: &PathSegment<'_>) {
+    let sp = span.with_hi(segment.ident.span.lo());
+    // remove the trailing ::
+    let span_without_last_segment = match snippet_opt(cx, sp) {
+        Some(snippet) => match snippet.rfind("::") {
+            Some(bidx) => sp.with_hi(sp.lo() + BytePos(bidx as u32)),
+            None => sp,
+        },
+        None => sp,
+    };
+    span_lint(cx, span_without_last_segment);
+}
+
+fn span_lint_on_path_until_last_segment(cx: &LateContext<'_>, path: &Path<'_>) {
+    if path.segments.len() > 1 {
+        span_lint_until_last_segment(cx, path.span, path.segments.last().unwrap());
+    }
+}
+
+fn span_lint_on_qpath_resolved(cx: &LateContext<'_>, qpath: &QPath<'_>, until_last_segment: bool) {
+    if let QPath::Resolved(_, path) = qpath {
+        if until_last_segment {
+            span_lint_on_path_until_last_segment(cx, path);
+        } else {
+            span_lint(cx, path.span);
         }
+    }
+}
 
-        walk_path(self, path);
+fn ty_from_hir_id<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Ty<'tcx> {
+    if let Some(Node::Ty(hir_ty)) = cx.tcx.hir().find(hir_id) {
+        hir_ty_to_ty(cx.tcx, hir_ty)
+    } else {
+        unreachable!("This function should only be called with `HirId`s that are for sure `Node::Ty`")
     }
+}
 
-    fn visit_item(&mut self, item: &'tcx Item<'_>) {
-        match item.kind {
-            ItemKind::Use(..)
-            | ItemKind::Static(..)
-            | ItemKind::Enum(..)
-            | ItemKind::Struct(..)
-            | ItemKind::Union(..)
-            | ItemKind::Impl { .. }
-            | ItemKind::Fn(..) => {
-                // Don't check statements that shadow `Self` or where `Self` can't be used
-            },
-            _ => walk_item(self, item),
+fn in_impl(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> bool {
+    let map = cx.tcx.hir();
+    let parent = map.get_parent_node(hir_ty.hir_id);
+    if_chain! {
+        if let Some(Node::Item(item)) = map.find(parent);
+        if let ItemKind::Impl { .. } = item.kind;
+        then {
+            true
+        } else {
+            false
         }
     }
+}
 
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::All(self.cx.tcx.hir())
+fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool {
+    if_chain! {
+        if TyS::same_type(ty, self_ty);
+        if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
+        then {
+            !matches!(path.res, def::Res::SelfTy(..))
+        } else {
+            false
+        }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
deleted file mode 100644 (file)
index 9ff7ef7..0000000
+++ /dev/null
@@ -1,579 +0,0 @@
-//! Utilities for manipulating and extracting information from `rustc_ast::ast`.
-//!
-//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s.
-
-#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
-
-use crate::utils::{both, over};
-use rustc_ast::ptr::P;
-use rustc_ast::{self as ast, *};
-use rustc_span::symbol::Ident;
-use std::mem;
-
-pub mod ident_iter;
-pub use ident_iter::IdentIter;
-
-pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool {
-    use BinOpKind::*;
-    matches!(
-        kind,
-        Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr
-    )
-}
-
-/// Checks if each element in the first slice is contained within the latter as per `eq_fn`.
-pub fn unordered_over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
-    left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r)))
-}
-
-pub fn eq_id(l: Ident, r: Ident) -> bool {
-    l.name == r.name
-}
-
-pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
-    use PatKind::*;
-    match (&l.kind, &r.kind) {
-        (Paren(l), _) => eq_pat(l, r),
-        (_, Paren(r)) => eq_pat(l, r),
-        (Wild, Wild) | (Rest, Rest) => true,
-        (Lit(l), Lit(r)) => eq_expr(l, r),
-        (Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)),
-        (Range(lf, lt, le), Range(rf, rt, re)) => {
-            eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node)
-        },
-        (Box(l), Box(r))
-        | (Ref(l, Mutability::Not), Ref(r, Mutability::Not))
-        | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
-        (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
-        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
-        (TupleStruct(lp, lfs), TupleStruct(rp, rfs)) => eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)),
-        (Struct(lp, lfs, lr), Struct(rp, rfs, rr)) => {
-            lr == rr && eq_path(lp, rp) && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
-        },
-        (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
-        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
-        _ => false,
-    }
-}
-
-pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool {
-    match (l, r) {
-        (RangeEnd::Excluded, RangeEnd::Excluded) => true,
-        (RangeEnd::Included(l), RangeEnd::Included(r)) => {
-            matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq)
-        },
-        _ => false,
-    }
-}
-
-pub fn eq_field_pat(l: &FieldPat, r: &FieldPat) -> bool {
-    l.is_placeholder == r.is_placeholder
-        && eq_id(l.ident, r.ident)
-        && eq_pat(&l.pat, &r.pat)
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
-}
-
-pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool {
-    l.position == r.position && eq_ty(&l.ty, &r.ty)
-}
-
-pub fn eq_path(l: &Path, r: &Path) -> bool {
-    over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r))
-}
-
-pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool {
-    eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r))
-}
-
-pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool {
-    match (l, r) {
-        (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => {
-            over(&l.args, &r.args, |l, r| eq_angle_arg(l, r))
-        },
-        (GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => {
-            over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output)
-        },
-        _ => false,
-    }
-}
-
-pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool {
-    match (l, r) {
-        (AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r),
-        (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_constraint(l, r),
-        _ => false,
-    }
-}
-
-pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool {
-    match (l, r) {
-        (GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident),
-        (GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r),
-        (GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value),
-        _ => false,
-    }
-}
-
-pub fn eq_expr_opt(l: &Option<P<Expr>>, r: &Option<P<Expr>>) -> bool {
-    both(l, r, |l, r| eq_expr(l, r))
-}
-
-pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool {
-    match (l, r) {
-        (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb),
-        (StructRest::Rest(_), StructRest::Rest(_)) | (StructRest::None, StructRest::None) => true,
-        _ => false,
-    }
-}
-
-pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
-    use ExprKind::*;
-    if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) {
-        return false;
-    }
-    match (&l.kind, &r.kind) {
-        (Paren(l), _) => eq_expr(l, r),
-        (_, Paren(r)) => eq_expr(l, r),
-        (Err, Err) => true,
-        (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r),
-        (Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)),
-        (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value),
-        (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
-        (MethodCall(lc, la, _), MethodCall(rc, ra, _)) => eq_path_seg(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
-        (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr),
-        (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
-        (Lit(l), Lit(r)) => l.kind == r.kind,
-        (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
-        (Let(lp, le), Let(rp, re)) => eq_pat(lp, rp) && eq_expr(le, re),
-        (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re),
-        (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt),
-        (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => {
-            eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt)
-        },
-        (Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt),
-        (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb),
-        (TryBlock(l), TryBlock(r)) => eq_block(l, r),
-        (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r),
-        (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re),
-        (Continue(ll), Continue(rl)) => eq_label(ll, rl),
-        (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2),
-        (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
-        (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
-        (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, |l, r| eq_arm(l, r)),
-        (Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => {
-            lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb)
-        },
-        (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb),
-        (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
-        (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
-        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
-        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
-        (Struct(lp, lfs, lb), Struct(rp, rfs, rb)) => {
-            eq_path(lp, rp) && eq_struct_rest(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r))
-        },
-        _ => false,
-    }
-}
-
-pub fn eq_field(l: &Field, r: &Field) -> bool {
-    l.is_placeholder == r.is_placeholder
-        && eq_id(l.ident, r.ident)
-        && eq_expr(&l.expr, &r.expr)
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
-}
-
-pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
-    l.is_placeholder == r.is_placeholder
-        && eq_pat(&l.pat, &r.pat)
-        && eq_expr(&l.body, &r.body)
-        && eq_expr_opt(&l.guard, &r.guard)
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
-}
-
-pub fn eq_label(l: &Option<Label>, r: &Option<Label>) -> bool {
-    both(l, r, |l, r| eq_id(l.ident, r.ident))
-}
-
-pub fn eq_block(l: &Block, r: &Block) -> bool {
-    l.rules == r.rules && over(&l.stmts, &r.stmts, |l, r| eq_stmt(l, r))
-}
-
-pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
-    use StmtKind::*;
-    match (&l.kind, &r.kind) {
-        (Local(l), Local(r)) => {
-            eq_pat(&l.pat, &r.pat)
-                && both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
-                && eq_expr_opt(&l.init, &r.init)
-                && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
-        },
-        (Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
-        (Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r),
-        (Empty, Empty) => true,
-        (MacCall(l), MacCall(r)) => {
-            l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
-        },
-        _ => false,
-    }
-}
-
-pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool {
-    eq_id(l.ident, r.ident)
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
-        && eq_vis(&l.vis, &r.vis)
-        && eq_kind(&l.kind, &r.kind)
-}
-
-pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
-    use ItemKind::*;
-    match (l, r) {
-        (ExternCrate(l), ExternCrate(r)) => l == r,
-        (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 FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, 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)) => lu == ru && match (lmk, rmk) {
-            (ModKind::Loaded(litems, linline, _), ModKind::Loaded(ritems, rinline, _)) =>
-                linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind)),
-            (ModKind::Unloaded, ModKind::Unloaded) => true,
-            _ => false,
-        },
-        (ForeignMod(l), ForeignMod(r)) => {
-            both(&l.abi, &r.abi, |l, r| eq_str_lit(l, r))
-                && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
-        },
-        (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
-            eq_defaultness(*ld, *rd)
-                && eq_generics(lg, rg)
-                && over(lb, rb, |l, r| eq_generic_bound(l, r))
-                && both(lt, rt, |l, r| eq_ty(l, r))
-        },
-        (Enum(le, lg), Enum(re, rg)) => {
-            over(&le.variants, &re.variants, |l, r| eq_variant(l, r)) && eq_generics(lg, rg)
-        },
-        (Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => {
-            eq_variant_data(lv, rv) && eq_generics(lg, rg)
-        },
-        (Trait(box TraitKind(la, lu, lg, lb, li)), Trait(box TraitKind(ra, ru, rg, rb, ri))) => {
-            la == ra
-                && matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
-                && eq_generics(lg, rg)
-                && over(lb, rb, |l, r| eq_generic_bound(l, r))
-                && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
-        },
-        (TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, |l, r| eq_generic_bound(l, r)),
-        (
-            Impl(box ImplKind {
-                unsafety: lu,
-                polarity: lp,
-                defaultness: ld,
-                constness: lc,
-                generics: lg,
-                of_trait: lot,
-                self_ty: lst,
-                items: li,
-            }),
-            Impl(box ImplKind {
-                unsafety: ru,
-                polarity: rp,
-                defaultness: rd,
-                constness: rc,
-                generics: rg,
-                of_trait: rot,
-                self_ty: rst,
-                items: ri,
-            }),
-        ) => {
-            matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
-                && matches!(lp, ImplPolarity::Positive) == matches!(rp, ImplPolarity::Positive)
-                && eq_defaultness(*ld, *rd)
-                && matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No)
-                && eq_generics(lg, rg)
-                && both(lot, rot, |l, r| eq_path(&l.path, &r.path))
-                && eq_ty(lst, rst)
-                && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
-        },
-        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
-        (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_mac_args(&l.body, &r.body),
-        _ => false,
-    }
-}
-
-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 FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, 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 TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
-            eq_defaultness(*ld, *rd)
-                && eq_generics(lg, rg)
-                && over(lb, rb, |l, r| eq_generic_bound(l, r))
-                && both(lt, rt, |l, r| eq_ty(l, r))
-        },
-        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
-        _ => false,
-    }
-}
-
-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 FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, 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 TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
-            eq_defaultness(*ld, *rd)
-                && eq_generics(lg, rg)
-                && over(lb, rb, |l, r| eq_generic_bound(l, r))
-                && both(lt, rt, |l, r| eq_ty(l, r))
-        },
-        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
-        _ => false,
-    }
-}
-
-pub fn eq_variant(l: &Variant, r: &Variant) -> bool {
-    l.is_placeholder == r.is_placeholder
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
-        && eq_vis(&l.vis, &r.vis)
-        && eq_id(l.ident, r.ident)
-        && eq_variant_data(&l.data, &r.data)
-        && both(&l.disr_expr, &r.disr_expr, |l, r| eq_expr(&l.value, &r.value))
-}
-
-pub fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool {
-    use VariantData::*;
-    match (l, r) {
-        (Unit(_), Unit(_)) => true,
-        (Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, |l, r| eq_struct_field(l, r)),
-        _ => false,
-    }
-}
-
-pub fn eq_struct_field(l: &StructField, r: &StructField) -> bool {
-    l.is_placeholder == r.is_placeholder
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
-        && eq_vis(&l.vis, &r.vis)
-        && both(&l.ident, &r.ident, |l, r| eq_id(*l, *r))
-        && eq_ty(&l.ty, &r.ty)
-}
-
-pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool {
-    eq_fn_decl(&l.decl, &r.decl) && eq_fn_header(&l.header, &r.header)
-}
-
-pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
-    matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No)
-        && l.asyncness.is_async() == r.asyncness.is_async()
-        && matches!(l.constness, Const::No) == matches!(r.constness, Const::No)
-        && eq_ext(&l.ext, &r.ext)
-}
-
-pub fn eq_generics(l: &Generics, r: &Generics) -> bool {
-    over(&l.params, &r.params, |l, r| eq_generic_param(l, r))
-        && over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| {
-            eq_where_predicate(l, r)
-        })
-}
-
-pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool {
-    use WherePredicate::*;
-    match (l, r) {
-        (BoundPredicate(l), BoundPredicate(r)) => {
-            over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
-                eq_generic_param(l, r)
-            }) && eq_ty(&l.bounded_ty, &r.bounded_ty)
-                && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
-        },
-        (RegionPredicate(l), RegionPredicate(r)) => {
-            eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
-        },
-        (EqPredicate(l), EqPredicate(r)) => eq_ty(&l.lhs_ty, &r.lhs_ty) && eq_ty(&l.rhs_ty, &r.rhs_ty),
-        _ => false,
-    }
-}
-
-pub fn eq_use_tree(l: &UseTree, r: &UseTree) -> bool {
-    eq_path(&l.prefix, &r.prefix) && eq_use_tree_kind(&l.kind, &r.kind)
-}
-
-pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
-    use UseTreeKind::*;
-    match (l, r) {
-        (Glob, Glob) => true,
-        (Simple(l, _, _), Simple(r, _, _)) => both(l, r, |l, r| eq_id(*l, *r)),
-        (Nested(l), Nested(r)) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)),
-        _ => false,
-    }
-}
-
-pub fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool {
-    eq_expr(&l.value, &r.value)
-}
-
-pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
-    matches!(
-        (l, r),
-        (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_))
-    )
-}
-
-pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool {
-    use VisibilityKind::*;
-    match (&l.kind, &r.kind) {
-        (Public, Public) | (Inherited, Inherited) | (Crate(_), Crate(_)) => true,
-        (Restricted { path: l, .. }, Restricted { path: r, .. }) => eq_path(l, r),
-        _ => false,
-    }
-}
-
-pub fn eq_fn_decl(l: &FnDecl, r: &FnDecl) -> bool {
-    eq_fn_ret_ty(&l.output, &r.output)
-        && over(&l.inputs, &r.inputs, |l, r| {
-            l.is_placeholder == r.is_placeholder
-                && eq_pat(&l.pat, &r.pat)
-                && eq_ty(&l.ty, &r.ty)
-                && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
-        })
-}
-
-pub fn eq_fn_ret_ty(l: &FnRetTy, r: &FnRetTy) -> bool {
-    match (l, r) {
-        (FnRetTy::Default(_), FnRetTy::Default(_)) => true,
-        (FnRetTy::Ty(l), FnRetTy::Ty(r)) => eq_ty(l, r),
-        _ => false,
-    }
-}
-
-pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
-    use TyKind::*;
-    match (&l.kind, &r.kind) {
-        (Paren(l), _) => eq_ty(l, r),
-        (_, Paren(r)) => eq_ty(l, r),
-        (Never, Never) | (Infer, Infer) | (ImplicitSelf, ImplicitSelf) | (Err, Err) | (CVarArgs, CVarArgs) => true,
-        (Slice(l), Slice(r)) => eq_ty(l, r),
-        (Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value),
-        (Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty),
-        (Rptr(ll, l), Rptr(rl, r)) => {
-            both(ll, rl, |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty)
-        },
-        (BareFn(l), BareFn(r)) => {
-            l.unsafety == r.unsafety
-                && eq_ext(&l.ext, &r.ext)
-                && over(&l.generic_params, &r.generic_params, |l, r| eq_generic_param(l, r))
-                && eq_fn_decl(&l.decl, &r.decl)
-        },
-        (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
-        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
-        (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, |l, r| eq_generic_bound(l, r)),
-        (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, |l, r| eq_generic_bound(l, r)),
-        (Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
-        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
-        _ => false,
-    }
-}
-
-pub fn eq_ext(l: &Extern, r: &Extern) -> bool {
-    use Extern::*;
-    match (l, r) {
-        (None, None) | (Implicit, Implicit) => true,
-        (Explicit(l), Explicit(r)) => eq_str_lit(l, r),
-        _ => false,
-    }
-}
-
-pub fn eq_str_lit(l: &StrLit, r: &StrLit) -> bool {
-    l.style == r.style && l.symbol == r.symbol && l.suffix == r.suffix
-}
-
-pub fn eq_poly_ref_trait(l: &PolyTraitRef, r: &PolyTraitRef) -> bool {
-    eq_path(&l.trait_ref.path, &r.trait_ref.path)
-        && over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
-            eq_generic_param(l, r)
-        })
-}
-
-pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
-    use GenericParamKind::*;
-    l.is_placeholder == r.is_placeholder
-        && eq_id(l.ident, r.ident)
-        && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
-        && match (&l.kind, &r.kind) {
-            (Lifetime, Lifetime) => true,
-            (Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)),
-            (
-                Const {
-                    ty: lt,
-                    kw_span: _,
-                    default: ld,
-                },
-                Const {
-                    ty: rt,
-                    kw_span: _,
-                    default: rd,
-                },
-            ) => eq_ty(lt, rt) && both(ld, rd, |ld, rd| eq_anon_const(ld, rd)),
-            _ => false,
-        }
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
-}
-
-pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool {
-    use GenericBound::*;
-    match (l, r) {
-        (Trait(ptr1, tbm1), Trait(ptr2, tbm2)) => tbm1 == tbm2 && eq_poly_ref_trait(ptr1, ptr2),
-        (Outlives(l), Outlives(r)) => eq_id(l.ident, r.ident),
-        _ => false,
-    }
-}
-
-pub fn eq_assoc_constraint(l: &AssocTyConstraint, r: &AssocTyConstraint) -> bool {
-    use AssocTyConstraintKind::*;
-    eq_id(l.ident, r.ident)
-        && match (&l.kind, &r.kind) {
-            (Equality { ty: l }, Equality { ty: r }) => eq_ty(l, r),
-            (Bound { bounds: l }, Bound { bounds: r }) => over(l, r, |l, r| eq_generic_bound(l, r)),
-            _ => false,
-        }
-}
-
-pub fn eq_mac_call(l: &MacCall, r: &MacCall) -> bool {
-    eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args)
-}
-
-pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
-    use AttrKind::*;
-    l.style == r.style
-        && match (&l.kind, &r.kind) {
-            (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
-            (Normal(l, _), Normal(r, _)) => eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args),
-            _ => false,
-        }
-}
-
-pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
-    use MacArgs::*;
-    match (l, r) {
-        (Empty, Empty) => true,
-        (Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts),
-        (Eq(_, lt), Eq(_, rt)) => lt.kind == rt.kind,
-        _ => false,
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/utils/ast_utils/ident_iter.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils/ident_iter.rs
deleted file mode 100644 (file)
index eefcbab..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-use core::iter::FusedIterator;
-use rustc_ast::visit::{walk_attribute, walk_expr, Visitor};
-use rustc_ast::{Attribute, Expr};
-use rustc_span::symbol::Ident;
-
-pub struct IdentIter(std::vec::IntoIter<Ident>);
-
-impl Iterator for IdentIter {
-    type Item = Ident;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.0.next()
-    }
-}
-
-impl FusedIterator for IdentIter {}
-
-impl From<&Expr> for IdentIter {
-    fn from(expr: &Expr) -> Self {
-        let mut visitor = IdentCollector::default();
-
-        walk_expr(&mut visitor, expr);
-
-        IdentIter(visitor.0.into_iter())
-    }
-}
-
-impl From<&Attribute> for IdentIter {
-    fn from(attr: &Attribute) -> Self {
-        let mut visitor = IdentCollector::default();
-
-        walk_attribute(&mut visitor, attr);
-
-        IdentIter(visitor.0.into_iter())
-    }
-}
-
-#[derive(Default)]
-struct IdentCollector(Vec<Ident>);
-
-impl Visitor<'_> for IdentCollector {
-    fn visit_ident(&mut self, ident: Ident) {
-        self.0.push(ident);
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/utils/attrs.rs b/src/tools/clippy/clippy_lints/src/utils/attrs.rs
deleted file mode 100644 (file)
index 8d28421..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-use rustc_ast::ast;
-use rustc_errors::Applicability;
-use rustc_session::Session;
-use rustc_span::sym;
-use std::str::FromStr;
-
-/// Deprecation status of attributes known by Clippy.
-#[allow(dead_code)]
-pub enum DeprecationStatus {
-    /// Attribute is deprecated
-    Deprecated,
-    /// Attribute is deprecated and was replaced by the named attribute
-    Replaced(&'static str),
-    None,
-}
-
-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),
-];
-
-pub struct LimitStack {
-    stack: Vec<u64>,
-}
-
-impl Drop for LimitStack {
-    fn drop(&mut self) {
-        assert_eq!(self.stack.len(), 1);
-    }
-}
-
-impl LimitStack {
-    #[must_use]
-    pub fn new(limit: u64) -> Self {
-        Self { stack: vec![limit] }
-    }
-    pub fn limit(&self) -> u64 {
-        *self.stack.last().expect("there should always be a value in the stack")
-    }
-    pub fn push_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
-        let stack = &mut self.stack;
-        parse_attrs(sess, attrs, name, |val| stack.push(val));
-    }
-    pub fn pop_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
-        let stack = &mut self.stack;
-        parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val)));
-    }
-}
-
-pub fn get_attr<'a>(
-    sess: &'a Session,
-    attrs: &'a [ast::Attribute],
-    name: &'static str,
-) -> impl Iterator<Item = &'a ast::Attribute> {
-    attrs.iter().filter(move |attr| {
-        let attr = if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
-            attr
-        } else {
-            return false;
-        };
-        let attr_segments = &attr.path.segments;
-        if attr_segments.len() == 2 && attr_segments[0].ident.name == sym::clippy {
-            BUILTIN_ATTRIBUTES
-                .iter()
-                .find_map(|&(builtin_name, ref deprecation_status)| {
-                    if attr_segments[1].ident.name.as_str() == builtin_name {
-                        Some(deprecation_status)
-                    } else {
-                        None
-                    }
-                })
-                .map_or_else(
-                    || {
-                        sess.span_err(attr_segments[1].ident.span, "usage of unknown attribute");
-                        false
-                    },
-                    |deprecation_status| {
-                        let mut diag =
-                            sess.struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute");
-                        match *deprecation_status {
-                            DeprecationStatus::Deprecated => {
-                                diag.emit();
-                                false
-                            },
-                            DeprecationStatus::Replaced(new_name) => {
-                                diag.span_suggestion(
-                                    attr_segments[1].ident.span,
-                                    "consider using",
-                                    new_name.to_string(),
-                                    Applicability::MachineApplicable,
-                                );
-                                diag.emit();
-                                false
-                            },
-                            DeprecationStatus::None => {
-                                diag.cancel();
-                                attr_segments[1].ident.name.as_str() == name
-                            },
-                        }
-                    },
-                )
-        } else {
-            false
-        }
-    })
-}
-
-fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'static str, mut f: F) {
-    for attr in get_attr(sess, attrs, name) {
-        if let Some(ref value) = attr.value_str() {
-            if let Ok(value) = FromStr::from_str(&value.as_str()) {
-                f(value)
-            } else {
-                sess.span_err(attr.span, "not a number");
-            }
-        } else {
-            sess.span_err(attr.span, "bad clippy attribute");
-        }
-    }
-}
-
-pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
-    let mut unique_attr = None;
-    for attr in get_attr(sess, attrs, name) {
-        match attr.style {
-            ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()),
-            ast::AttrStyle::Inner => {
-                sess.struct_span_err(attr.span, &format!("`{}` is defined multiple times", name))
-                    .span_note(unique_attr.as_ref().unwrap().span, "first definition found here")
-                    .emit();
-            },
-            ast::AttrStyle::Outer => {
-                sess.span_err(attr.span, &format!("`{}` cannot be an outer attribute", name));
-            },
-        }
-    }
-    unique_attr
-}
-
-/// Return true if the attributes contain any of `proc_macro`,
-/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
-pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
-    attrs.iter().any(|attr| sess.is_proc_macro_attr(attr))
-}
diff --git a/src/tools/clippy/clippy_lints/src/utils/camel_case.rs b/src/tools/clippy/clippy_lints/src/utils/camel_case.rs
deleted file mode 100644 (file)
index 4192a26..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/// Returns the index of the character after the first camel-case component of `s`.
-#[must_use]
-pub fn until(s: &str) -> usize {
-    let mut iter = s.char_indices();
-    if let Some((_, first)) = iter.next() {
-        if !first.is_uppercase() {
-            return 0;
-        }
-    } else {
-        return 0;
-    }
-    let mut up = true;
-    let mut last_i = 0;
-    for (i, c) in iter {
-        if up {
-            if c.is_lowercase() {
-                up = false;
-            } else {
-                return last_i;
-            }
-        } else if c.is_uppercase() {
-            up = true;
-            last_i = i;
-        } else if !c.is_lowercase() {
-            return i;
-        }
-    }
-    if up {
-        last_i
-    } else {
-        s.len()
-    }
-}
-
-/// Returns index of the last camel-case component of `s`.
-#[must_use]
-pub fn from(s: &str) -> usize {
-    let mut iter = s.char_indices().rev();
-    if let Some((_, first)) = iter.next() {
-        if !first.is_lowercase() {
-            return s.len();
-        }
-    } else {
-        return s.len();
-    }
-    let mut down = true;
-    let mut last_i = s.len();
-    for (i, c) in iter {
-        if down {
-            if c.is_uppercase() {
-                down = false;
-                last_i = i;
-            } else if !c.is_lowercase() {
-                return last_i;
-            }
-        } else if c.is_lowercase() {
-            down = true;
-        } else {
-            return last_i;
-        }
-    }
-    last_i
-}
-
-#[cfg(test)]
-mod test {
-    use super::{from, until};
-
-    #[test]
-    fn from_full() {
-        assert_eq!(from("AbcDef"), 0);
-        assert_eq!(from("Abc"), 0);
-    }
-
-    #[test]
-    fn from_partial() {
-        assert_eq!(from("abcDef"), 3);
-        assert_eq!(from("aDbc"), 1);
-    }
-
-    #[test]
-    fn from_not() {
-        assert_eq!(from("AbcDef_"), 7);
-        assert_eq!(from("AbcDD"), 5);
-    }
-
-    #[test]
-    fn from_caps() {
-        assert_eq!(from("ABCD"), 4);
-    }
-
-    #[test]
-    fn until_full() {
-        assert_eq!(until("AbcDef"), 6);
-        assert_eq!(until("Abc"), 3);
-    }
-
-    #[test]
-    fn until_not() {
-        assert_eq!(until("abcDef"), 0);
-        assert_eq!(until("aDbc"), 0);
-    }
-
-    #[test]
-    fn until_partial() {
-        assert_eq!(until("AbcDef_"), 6);
-        assert_eq!(until("CallTypeC"), 8);
-        assert_eq!(until("AbcDD"), 3);
-    }
-
-    #[test]
-    fn until_caps() {
-        assert_eq!(until("ABCD"), 0);
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/utils/comparisons.rs b/src/tools/clippy/clippy_lints/src/utils/comparisons.rs
deleted file mode 100644 (file)
index 7a18d5e..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-//! Utility functions about comparison operators.
-
-#![deny(clippy::missing_docs_in_private_items)]
-
-use rustc_hir::{BinOpKind, Expr};
-
-#[derive(PartialEq, Eq, Debug, Copy, Clone)]
-/// Represent a normalized comparison operator.
-pub enum Rel {
-    /// `<`
-    Lt,
-    /// `<=`
-    Le,
-    /// `==`
-    Eq,
-    /// `!=`
-    Ne,
-}
-
-/// Put the expression in the form  `lhs < rhs`, `lhs <= rhs`, `lhs == rhs` or
-/// `lhs != rhs`.
-pub fn normalize_comparison<'a>(
-    op: BinOpKind,
-    lhs: &'a Expr<'a>,
-    rhs: &'a Expr<'a>,
-) -> Option<(Rel, &'a Expr<'a>, &'a Expr<'a>)> {
-    match op {
-        BinOpKind::Lt => Some((Rel::Lt, lhs, rhs)),
-        BinOpKind::Le => Some((Rel::Le, lhs, rhs)),
-        BinOpKind::Gt => Some((Rel::Lt, rhs, lhs)),
-        BinOpKind::Ge => Some((Rel::Le, rhs, lhs)),
-        BinOpKind::Eq => Some((Rel::Eq, rhs, lhs)),
-        BinOpKind::Ne => Some((Rel::Ne, rhs, lhs)),
-        _ => None,
-    }
-}
index 7d7b35c21680d907bf4b772570b2126e7778679d..9139a0966c55785f335c602cb427cd75fdaf5194 100644 (file)
@@ -126,7 +126,7 @@ fn $config() -> $Ty {
         "NaN", "NaNs",
         "OAuth", "GraphQL",
         "OCaml",
-        "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap",
+        "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
         "WebGL",
         "TensorFlow",
         "TrueType",
@@ -173,6 +173,8 @@ fn $config() -> $Ty {
     (disallowed_methods, "disallowed_methods": Vec<String>, Vec::<String>::new()),
     /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
     (unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true),
+    /// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other
+    (upper_case_acronyms_aggressive, "upper_case_acronyms_aggressive": bool, false),
     /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest.
     (cargo_ignore_publish, "cargo_ignore_publish": bool, false),
 }
diff --git a/src/tools/clippy/clippy_lints/src/utils/diagnostics.rs b/src/tools/clippy/clippy_lints/src/utils/diagnostics.rs
deleted file mode 100644 (file)
index 269be21..0000000
+++ /dev/null
@@ -1,226 +0,0 @@
-//! Clippy wrappers around rustc's diagnostic functions.
-
-use rustc_errors::{Applicability, DiagnosticBuilder};
-use rustc_hir::HirId;
-use rustc_lint::{LateContext, Lint, LintContext};
-use rustc_span::source_map::{MultiSpan, Span};
-use std::env;
-
-fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) {
-    if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() {
-        diag.help(&format!(
-            "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}",
-            &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
-                // extract just major + minor version and ignore patch versions
-                format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap())
-            }),
-            lint.name_lower().replacen("clippy::", "", 1)
-        ));
-    }
-}
-
-/// Emit a basic lint message with a `msg` and a `span`.
-///
-/// This is the most primitive of our lint emission methods and can
-/// be a good way to get a new lint started.
-///
-/// Usually it's nicer to provide more context for lint messages.
-/// Be sure the output is understandable when you use this method.
-///
-/// # Example
-///
-/// ```ignore
-/// error: usage of mem::forget on Drop type
-///   --> $DIR/mem_forget.rs:17:5
-///    |
-/// 17 |     std::mem::forget(seven);
-///    |     ^^^^^^^^^^^^^^^^^^^^^^^
-/// ```
-pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<MultiSpan>, msg: &str) {
-    cx.struct_span_lint(lint, sp, |diag| {
-        let mut diag = diag.build(msg);
-        docs_link(&mut diag, lint);
-        diag.emit();
-    });
-}
-
-/// Same as `span_lint` but with an extra `help` message.
-///
-/// Use this if you want to provide some general help but
-/// can't provide a specific machine applicable suggestion.
-///
-/// The `help` message can be optionally attached to a `Span`.
-///
-/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
-///
-/// # Example
-///
-/// ```ignore
-/// error: constant division of 0.0 with 0.0 will always result in NaN
-///   --> $DIR/zero_div_zero.rs:6:25
-///    |
-/// 6  |     let other_f64_nan = 0.0f64 / 0.0;
-///    |                         ^^^^^^^^^^^^
-///    |
-///    = help: Consider using `f64::NAN` if you would like a constant representing NaN
-/// ```
-pub fn span_lint_and_help<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    span: Span,
-    msg: &str,
-    help_span: Option<Span>,
-    help: &str,
-) {
-    cx.struct_span_lint(lint, span, |diag| {
-        let mut diag = diag.build(msg);
-        if let Some(help_span) = help_span {
-            diag.span_help(help_span, help);
-        } else {
-            diag.help(help);
-        }
-        docs_link(&mut diag, lint);
-        diag.emit();
-    });
-}
-
-/// Like `span_lint` but with a `note` section instead of a `help` message.
-///
-/// The `note` message is presented separately from the main lint message
-/// and is attached to a specific span:
-///
-/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
-///
-/// # Example
-///
-/// ```ignore
-/// error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
-///   --> $DIR/drop_forget_ref.rs:10:5
-///    |
-/// 10 |     forget(&SomeStruct);
-///    |     ^^^^^^^^^^^^^^^^^^^
-///    |
-///    = note: `-D clippy::forget-ref` implied by `-D warnings`
-/// note: argument has type &SomeStruct
-///   --> $DIR/drop_forget_ref.rs:10:12
-///    |
-/// 10 |     forget(&SomeStruct);
-///    |            ^^^^^^^^^^^
-/// ```
-pub fn span_lint_and_note<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    span: impl Into<MultiSpan>,
-    msg: &str,
-    note_span: Option<Span>,
-    note: &str,
-) {
-    cx.struct_span_lint(lint, span, |diag| {
-        let mut diag = diag.build(msg);
-        if let Some(note_span) = note_span {
-            diag.span_note(note_span, note);
-        } else {
-            diag.note(note);
-        }
-        docs_link(&mut diag, lint);
-        diag.emit();
-    });
-}
-
-/// Like `span_lint` but allows to add notes, help and suggestions using a closure.
-///
-/// If you need to customize your lint output a lot, use this function.
-/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
-pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
-where
-    F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
-{
-    cx.struct_span_lint(lint, sp, |diag| {
-        let mut diag = diag.build(msg);
-        f(&mut diag);
-        docs_link(&mut diag, lint);
-        diag.emit();
-    });
-}
-
-pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) {
-    cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
-        let mut diag = diag.build(msg);
-        docs_link(&mut diag, lint);
-        diag.emit();
-    });
-}
-
-pub fn span_lint_hir_and_then(
-    cx: &LateContext<'_>,
-    lint: &'static Lint,
-    hir_id: HirId,
-    sp: Span,
-    msg: &str,
-    f: impl FnOnce(&mut DiagnosticBuilder<'_>),
-) {
-    cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
-        let mut diag = diag.build(msg);
-        f(&mut diag);
-        docs_link(&mut diag, lint);
-        diag.emit();
-    });
-}
-
-/// Add a span lint with a suggestion on how to fix it.
-///
-/// These suggestions can be parsed by rustfix to allow it to automatically fix your code.
-/// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x >
-/// 2)"`.
-///
-/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
-///
-/// # Example
-///
-/// ```ignore
-/// error: This `.fold` can be more succinctly expressed as `.any`
-/// --> $DIR/methods.rs:390:13
-///     |
-/// 390 |     let _ = (0..3).fold(false, |acc, x| acc || x > 2);
-///     |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
-///     |
-///     = note: `-D fold-any` implied by `-D warnings`
-/// ```
-#[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))]
-pub fn span_lint_and_sugg<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    sp: Span,
-    msg: &str,
-    help: &str,
-    sugg: String,
-    applicability: Applicability,
-) {
-    span_lint_and_then(cx, lint, sp, msg, |diag| {
-        diag.span_suggestion(sp, help, sugg, applicability);
-    });
-}
-
-/// Create a suggestion made from several `span → replacement`.
-///
-/// Note: in the JSON format (used by `compiletest_rs`), the help message will
-/// appear once per
-/// replacement. In human-readable format though, it only appears once before
-/// the whole suggestion.
-pub fn multispan_sugg<I>(diag: &mut DiagnosticBuilder<'_>, help_msg: &str, sugg: I)
-where
-    I: IntoIterator<Item = (Span, String)>,
-{
-    multispan_sugg_with_applicability(diag, help_msg, Applicability::Unspecified, sugg)
-}
-
-pub fn multispan_sugg_with_applicability<I>(
-    diag: &mut DiagnosticBuilder<'_>,
-    help_msg: &str,
-    applicability: Applicability,
-    sugg: I,
-) where
-    I: IntoIterator<Item = (Span, String)>,
-{
-    diag.multipart_suggestion(help_msg, sugg.into_iter().collect(), applicability);
-}
diff --git a/src/tools/clippy/clippy_lints/src/utils/eager_or_lazy.rs b/src/tools/clippy/clippy_lints/src/utils/eager_or_lazy.rs
deleted file mode 100644 (file)
index 2f157c5..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-//! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa.
-//!
-//! Things to consider:
-//!  - has the expression side-effects?
-//!  - is the expression computationally expensive?
-//!
-//! See lints:
-//!  - unnecessary-lazy-evaluations
-//!  - or-fun-call
-//!  - option-if-let-else
-
-use crate::utils::{is_ctor_or_promotable_const_function, is_type_diagnostic_item, match_type, paths};
-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_lint::LateContext;
-use rustc_middle::hir::map::Map;
-
-/// 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(|expr| identify_some_pure_patterns(expr)),
-        ExprKind::Struct(_, fields, expr) => {
-            fields.iter().all(|f| identify_some_pure_patterns(f.expr))
-                && expr.map_or(true, |e| identify_some_pure_patterns(e))
-        },
-        ExprKind::Call(
-            &Expr {
-                kind:
-                    ExprKind::Path(QPath::Resolved(
-                        _,
-                        Path {
-                            res: Res::Def(DefKind::Ctor(..) | DefKind::Variant, ..),
-                            ..
-                        },
-                    )),
-                ..
-            },
-            args,
-        ) => args.iter().all(|expr| identify_some_pure_patterns(expr)),
-        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::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,
-    }
-}
-
-/// 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<'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_type))
-                        || match_type(self.cx, ty, &paths::BTREEMAP)
-                },
-                ExprKind::MethodCall(..) => true,
-                _ => false,
-            };
-
-            if call_found {
-                self.found |= true;
-            }
-
-            if !self.found {
-                intravisit::walk_expr(self, expr);
-            }
-        }
-
-        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-            NestedVisitorMap::None
-        }
-    }
-
-    let mut finder = FunCallFinder { cx, found: false };
-    finder.visit_expr(expr);
-    finder.found
-}
-
-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)
-}
-
-pub fn is_lazyness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    identify_some_potentially_expensive_patterns(cx, expr)
-}
diff --git a/src/tools/clippy/clippy_lints/src/utils/higher.rs b/src/tools/clippy/clippy_lints/src/utils/higher.rs
deleted file mode 100644 (file)
index 1cf1aa3..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-//! This module contains functions for retrieve the original AST from lowered
-//! `hir`.
-
-#![deny(clippy::missing_docs_in_private_items)]
-
-use crate::utils::{is_expn_of, match_def_path, paths};
-use if_chain::if_chain;
-use rustc_ast::ast;
-use rustc_hir as hir;
-use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp};
-use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
-
-/// Converts a hir binary operator to the corresponding `ast` type.
-#[must_use]
-pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
-    match op {
-        hir::BinOpKind::Eq => ast::BinOpKind::Eq,
-        hir::BinOpKind::Ge => ast::BinOpKind::Ge,
-        hir::BinOpKind::Gt => ast::BinOpKind::Gt,
-        hir::BinOpKind::Le => ast::BinOpKind::Le,
-        hir::BinOpKind::Lt => ast::BinOpKind::Lt,
-        hir::BinOpKind::Ne => ast::BinOpKind::Ne,
-        hir::BinOpKind::Or => ast::BinOpKind::Or,
-        hir::BinOpKind::Add => ast::BinOpKind::Add,
-        hir::BinOpKind::And => ast::BinOpKind::And,
-        hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd,
-        hir::BinOpKind::BitOr => ast::BinOpKind::BitOr,
-        hir::BinOpKind::BitXor => ast::BinOpKind::BitXor,
-        hir::BinOpKind::Div => ast::BinOpKind::Div,
-        hir::BinOpKind::Mul => ast::BinOpKind::Mul,
-        hir::BinOpKind::Rem => ast::BinOpKind::Rem,
-        hir::BinOpKind::Shl => ast::BinOpKind::Shl,
-        hir::BinOpKind::Shr => ast::BinOpKind::Shr,
-        hir::BinOpKind::Sub => ast::BinOpKind::Sub,
-    }
-}
-
-/// Represent a range akin to `ast::ExprKind::Range`.
-#[derive(Debug, Copy, Clone)]
-pub struct Range<'a> {
-    /// The lower bound of the range, or `None` for ranges such as `..X`.
-    pub start: Option<&'a hir::Expr<'a>>,
-    /// The upper bound of the range, or `None` for ranges such as `X..`.
-    pub end: Option<&'a hir::Expr<'a>>,
-    /// Whether the interval is open or closed.
-    pub limits: ast::RangeLimits,
-}
-
-/// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
-pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
-    /// Finds the field named `name` in the field. Always return `Some` for
-    /// convenience.
-    fn get_field<'c>(name: &str, fields: &'c [hir::Field<'_>]) -> Option<&'c hir::Expr<'c>> {
-        let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr;
-
-        Some(expr)
-    }
-
-    match expr.kind {
-        hir::ExprKind::Call(ref path, ref args)
-            if matches!(
-                path.kind,
-                hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _))
-            ) =>
-        {
-            Some(Range {
-                start: Some(&args[0]),
-                end: Some(&args[1]),
-                limits: ast::RangeLimits::Closed,
-            })
-        },
-        hir::ExprKind::Struct(ref path, ref fields, None) => match path {
-            hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range {
-                start: None,
-                end: None,
-                limits: ast::RangeLimits::HalfOpen,
-            }),
-            hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range {
-                start: Some(get_field("start", fields)?),
-                end: None,
-                limits: ast::RangeLimits::HalfOpen,
-            }),
-            hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range {
-                start: Some(get_field("start", fields)?),
-                end: Some(get_field("end", fields)?),
-                limits: ast::RangeLimits::HalfOpen,
-            }),
-            hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range {
-                start: None,
-                end: Some(get_field("end", fields)?),
-                limits: ast::RangeLimits::Closed,
-            }),
-            hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range {
-                start: None,
-                end: Some(get_field("end", fields)?),
-                limits: ast::RangeLimits::HalfOpen,
-            }),
-            _ => None,
-        },
-        _ => None,
-    }
-}
-
-/// Checks if a `let` statement is from a `for` loop desugaring.
-pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
-    // This will detect plain for-loops without an actual variable binding:
-    //
-    // ```
-    // for x in some_vec {
-    //     // do stuff
-    // }
-    // ```
-    if_chain! {
-        if let Some(ref expr) = local.init;
-        if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind;
-        then {
-            return true;
-        }
-    }
-
-    // This detects a variable binding in for loop to avoid `let_unit_value`
-    // lint (see issue #1964).
-    //
-    // ```
-    // for _ in vec![()] {
-    //     // anything
-    // }
-    // ```
-    if let hir::LocalSource::ForLoopDesugar = local.source {
-        return true;
-    }
-
-    false
-}
-
-/// Recover the essential nodes of a desugared for loop as well as the entire span:
-/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
-pub fn for_loop<'tcx>(
-    expr: &'tcx hir::Expr<'tcx>,
-) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>, Span)> {
-    if_chain! {
-        if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
-        if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind;
-        if iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none();
-        if let hir::ExprKind::Loop(ref block, ..) = arms[0].body.kind;
-        if block.expr.is_none();
-        if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
-        if let hir::StmtKind::Local(ref local) = let_stmt.kind;
-        if let hir::StmtKind::Expr(ref expr) = body.kind;
-        then {
-            return Some((&*local.pat, &iterargs[0], expr, arms[0].span));
-        }
-    }
-    None
-}
-
-/// Recover the essential nodes of a desugared while loop:
-/// `while cond { body }` becomes `(cond, body)`.
-pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> {
-    if_chain! {
-        if let hir::ExprKind::Loop(hir::Block { expr: Some(expr), .. }, _, hir::LoopSource::While, _) = &expr.kind;
-        if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind;
-        if let hir::ExprKind::DropTemps(cond) = &cond.kind;
-        if let [hir::Arm { body, .. }, ..] = &arms[..];
-        then {
-            return Some((cond, body));
-        }
-    }
-    None
-}
-
-/// Represent the pre-expansion arguments of a `vec!` invocation.
-pub enum VecArgs<'a> {
-    /// `vec![elem; len]`
-    Repeat(&'a hir::Expr<'a>, &'a hir::Expr<'a>),
-    /// `vec![a, b, c]`
-    Vec(&'a [hir::Expr<'a>]),
-}
-
-/// Returns the arguments of the `vec!` macro if this expression was expanded
-/// from `vec!`.
-pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option<VecArgs<'e>> {
-    if_chain! {
-        if let hir::ExprKind::Call(ref fun, ref args) = expr.kind;
-        if let hir::ExprKind::Path(ref qpath) = fun.kind;
-        if is_expn_of(fun.span, "vec").is_some();
-        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
-        then {
-            return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
-                // `vec![elem; size]` case
-                Some(VecArgs::Repeat(&args[0], &args[1]))
-            }
-            else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
-                // `vec![a, b, c]` case
-                if_chain! {
-                    if let hir::ExprKind::Box(ref boxed) = args[0].kind;
-                    if let hir::ExprKind::Array(ref args) = boxed.kind;
-                    then {
-                        return Some(VecArgs::Vec(&*args));
-                    }
-                }
-
-                None
-            }
-            else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() {
-                Some(VecArgs::Vec(&[]))
-            }
-            else {
-                None
-            };
-        }
-    }
-
-    None
-}
-
-/// Extract args from an assert-like macro.
-/// Currently working with:
-/// - `assert!`, `assert_eq!` and `assert_ne!`
-/// - `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!`
-/// For example:
-/// `assert!(expr)` will return Some([expr])
-/// `debug_assert_eq!(a, b)` will return Some([a, b])
-pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx Expr<'tcx>>> {
-    /// Try to match the AST for a pattern that contains a match, for example when two args are
-    /// compared
-    fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option<Vec<&Expr<'_>>> {
-        if_chain! {
-            if let ExprKind::Match(ref headerexpr, _, _) = &matchblock_expr.kind;
-            if let ExprKind::Tup([lhs, rhs]) = &headerexpr.kind;
-            if let ExprKind::AddrOf(BorrowKind::Ref, _, lhs) = lhs.kind;
-            if let ExprKind::AddrOf(BorrowKind::Ref, _, rhs) = rhs.kind;
-            then {
-                return Some(vec![lhs, rhs]);
-            }
-        }
-        None
-    }
-
-    if let ExprKind::Block(ref block, _) = e.kind {
-        if block.stmts.len() == 1 {
-            if let StmtKind::Semi(ref matchexpr) = block.stmts.get(0)?.kind {
-                // macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`)
-                if_chain! {
-                    if let ExprKind::If(ref clause, _, _)  = matchexpr.kind;
-                    if let ExprKind::Unary(UnOp::Not, condition) = clause.kind;
-                    then {
-                        return Some(vec![condition]);
-                    }
-                }
-
-                // debug macros with two args: `debug_assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
-                if_chain! {
-                    if let ExprKind::Block(ref matchblock,_) = matchexpr.kind;
-                    if let Some(ref matchblock_expr) = matchblock.expr;
-                    then {
-                        return ast_matchblock(matchblock_expr);
-                    }
-                }
-            }
-        } else if let Some(matchblock_expr) = block.expr {
-            // macros with two args: `assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
-            return ast_matchblock(&matchblock_expr);
-        }
-    }
-    None
-}
diff --git a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
deleted file mode 100644 (file)
index c5870dc..0000000
+++ /dev/null
@@ -1,808 +0,0 @@
-use crate::consts::{constant_context, constant_simple};
-use crate::utils::differing_macro_contexts;
-use rustc_ast::ast::InlineAsmTemplatePiece;
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_hir::{
-    BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat, FnRetTy,
-    GenericArg, GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path,
-    PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
-};
-use rustc_lint::LateContext;
-use rustc_middle::ich::StableHashingContextProvider;
-use rustc_middle::ty::TypeckResults;
-use rustc_span::Symbol;
-use std::hash::Hash;
-
-/// Type used to check whether two ast are the same. This is different from the
-/// operator
-/// `==` on ast types as this operator would compare true equality with ID and
-/// span.
-///
-/// Note that some expressions kinds are not considered but could be added.
-pub struct SpanlessEq<'a, 'tcx> {
-    /// Context used to evaluate constant expressions.
-    cx: &'a LateContext<'tcx>,
-    maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
-    allow_side_effects: bool,
-    expr_fallback: Option<Box<dyn Fn(&Expr<'_>, &Expr<'_>) -> bool + 'a>>,
-}
-
-impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
-    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
-        Self {
-            cx,
-            maybe_typeck_results: cx.maybe_typeck_results(),
-            allow_side_effects: true,
-            expr_fallback: None,
-        }
-    }
-
-    /// Consider expressions containing potential side effects as not equal.
-    pub fn deny_side_effects(self) -> Self {
-        Self {
-            allow_side_effects: false,
-            ..self
-        }
-    }
-
-    pub fn expr_fallback(self, expr_fallback: impl Fn(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
-        Self {
-            expr_fallback: Some(Box::new(expr_fallback)),
-            ..self
-        }
-    }
-
-    /// Checks whether two statements are the same.
-    pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
-        match (&left.kind, &right.kind) {
-            (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => {
-                self.eq_pat(&l.pat, &r.pat)
-                    && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r))
-                    && both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
-            },
-            (&StmtKind::Expr(ref l), &StmtKind::Expr(ref r)) | (&StmtKind::Semi(ref l), &StmtKind::Semi(ref r)) => {
-                self.eq_expr(l, r)
-            },
-            _ => false,
-        }
-    }
-
-    /// Checks whether two blocks are the same.
-    pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
-        over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r))
-            && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
-    }
-
-    #[allow(clippy::similar_names)]
-    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
-        if !self.allow_side_effects && differing_macro_contexts(left.span, right.span) {
-            return false;
-        }
-
-        if let Some(typeck_results) = self.maybe_typeck_results {
-            if let (Some(l), Some(r)) = (
-                constant_simple(self.cx, typeck_results, left),
-                constant_simple(self.cx, typeck_results, right),
-            ) {
-                if l == r {
-                    return true;
-                }
-            }
-        }
-
-        let is_eq = match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) {
-            (&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => {
-                lb == rb && l_mut == r_mut && self.eq_expr(le, re)
-            },
-            (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => {
-                both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
-            },
-            (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => {
-                self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
-            },
-            (&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => {
-                self.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
-            },
-            (&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r),
-            (&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref rr)) => {
-                l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
-                    || swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| {
-                        l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
-                    })
-            },
-            (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => {
-                both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
-                    && both(le, re, |l, r| self.eq_expr(l, r))
-            },
-            (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r),
-            (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
-                self.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
-            },
-            (&ExprKind::Cast(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt))
-            | (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => {
-                self.eq_expr(lx, rx) && self.eq_ty(lt, rt)
-            },
-            (&ExprKind::Field(ref l_f_exp, ref l_f_ident), &ExprKind::Field(ref r_f_exp, ref r_f_ident)) => {
-                l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp)
-            },
-            (&ExprKind::Index(ref la, ref li), &ExprKind::Index(ref ra, ref ri)) => {
-                self.eq_expr(la, ra) && self.eq_expr(li, ri)
-            },
-            (&ExprKind::If(ref lc, ref lt, ref le), &ExprKind::If(ref rc, ref rt, ref re)) => {
-                self.eq_expr(lc, rc) && self.eq_expr(&**lt, &**rt) && both(le, re, |l, r| self.eq_expr(l, r))
-            },
-            (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
-            (&ExprKind::Loop(ref lb, ref ll, ref lls, _), &ExprKind::Loop(ref rb, ref rl, ref rls, _)) => {
-                lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
-            },
-            (&ExprKind::Match(ref le, ref la, ref ls), &ExprKind::Match(ref re, ref ra, ref rs)) => {
-                ls == rs
-                    && self.eq_expr(le, re)
-                    && over(la, ra, |l, r| {
-                        self.eq_expr(&l.body, &r.body)
-                            && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r))
-                            && self.eq_pat(&l.pat, &r.pat)
-                    })
-            },
-            (&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => {
-                self.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args)
-            },
-            (&ExprKind::Repeat(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => {
-                let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(ll_id.body));
-                let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value);
-                let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(rl_id.body));
-                let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value);
-
-                self.eq_expr(le, re) && ll == rl
-            },
-            (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)),
-            (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r),
-            (&ExprKind::Struct(ref l_path, ref lf, ref lo), &ExprKind::Struct(ref r_path, ref rf, ref ro)) => {
-                self.eq_qpath(l_path, r_path)
-                    && both(lo, ro, |l, r| self.eq_expr(l, r))
-                    && over(lf, rf, |l, r| self.eq_field(l, r))
-            },
-            (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
-            (&ExprKind::Unary(l_op, ref le), &ExprKind::Unary(r_op, ref re)) => l_op == r_op && self.eq_expr(le, re),
-            (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
-            (&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re),
-            _ => false,
-        };
-        is_eq || self.expr_fallback.as_ref().map_or(false, |f| f(left, right))
-    }
-
-    fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
-        over(left, right, |l, r| self.eq_expr(l, r))
-    }
-
-    fn eq_field(&mut self, left: &Field<'_>, right: &Field<'_>) -> bool {
-        left.ident.name == right.ident.name && self.eq_expr(&left.expr, &right.expr)
-    }
-
-    fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
-        match (left, right) {
-            (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
-            (Guard::IfLet(lp, le), Guard::IfLet(rp, re)) => self.eq_pat(lp, rp) && self.eq_expr(le, re),
-            _ => false,
-        }
-    }
-
-    fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
-        match (left, right) {
-            (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt),
-            (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty, r_ty),
-            _ => false,
-        }
-    }
-
-    fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
-        left.name == right.name
-    }
-
-    pub fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool {
-        let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right);
-        li.name == ri.name && self.eq_pat(lp, rp)
-    }
-
-    /// Checks whether two patterns are the same.
-    pub fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
-        match (&left.kind, &right.kind) {
-            (&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r),
-            (&PatKind::Struct(ref lp, ref la, ..), &PatKind::Struct(ref rp, ref ra, ..)) => {
-                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_fieldpat(l, r))
-            },
-            (&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => {
-                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
-            },
-            (&PatKind::Binding(ref lb, .., ref li, ref lp), &PatKind::Binding(ref rb, .., ref ri, ref rp)) => {
-                lb == rb && li.name == ri.name && both(lp, rp, |l, r| self.eq_pat(l, r))
-            },
-            (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
-            (&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r),
-            (&PatKind::Tuple(ref l, ls), &PatKind::Tuple(ref r, rs)) => {
-                ls == rs && over(l, r, |l, r| self.eq_pat(l, r))
-            },
-            (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
-                both(ls, rs, |a, b| self.eq_expr(a, b)) && both(le, re, |a, b| self.eq_expr(a, b)) && (li == ri)
-            },
-            (&PatKind::Ref(ref le, ref lm), &PatKind::Ref(ref re, ref rm)) => lm == rm && self.eq_pat(le, re),
-            (&PatKind::Slice(ref ls, ref li, ref le), &PatKind::Slice(ref rs, ref ri, ref re)) => {
-                over(ls, rs, |l, r| self.eq_pat(l, r))
-                    && over(le, re, |l, r| self.eq_pat(l, r))
-                    && both(li, ri, |l, r| self.eq_pat(l, r))
-            },
-            (&PatKind::Wild, &PatKind::Wild) => true,
-            _ => false,
-        }
-    }
-
-    #[allow(clippy::similar_names)]
-    fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
-        match (left, right) {
-            (&QPath::Resolved(ref lty, ref lpath), &QPath::Resolved(ref rty, ref rpath)) => {
-                both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
-            },
-            (&QPath::TypeRelative(ref lty, ref lseg), &QPath::TypeRelative(ref rty, ref rseg)) => {
-                self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
-            },
-            (&QPath::LangItem(llang_item, _), &QPath::LangItem(rlang_item, _)) => llang_item == rlang_item,
-            _ => false,
-        }
-    }
-
-    fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
-        left.is_global() == right.is_global()
-            && over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r))
-    }
-
-    fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
-        if !(left.parenthesized || right.parenthesized) {
-            over(&left.args, &right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work
-                && over(&left.bindings, &right.bindings, |l, r| self.eq_type_binding(l, r))
-        } else if left.parenthesized && right.parenthesized {
-            over(left.inputs(), right.inputs(), |l, r| self.eq_ty(l, r))
-                && both(&Some(&left.bindings[0].ty()), &Some(&right.bindings[0].ty()), |l, r| {
-                    self.eq_ty(l, r)
-                })
-        } else {
-            false
-        }
-    }
-
-    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
-        left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r))
-    }
-
-    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
-        // The == of idents doesn't work with different contexts,
-        // we have to be explicit about hygiene
-        left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
-    }
-
-    pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
-        self.eq_ty_kind(&left.kind, &right.kind)
-    }
-
-    #[allow(clippy::similar_names)]
-    pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool {
-        match (left, right) {
-            (&TyKind::Slice(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec),
-            (&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => {
-                let old_maybe_typeck_results = self.maybe_typeck_results;
-
-                let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(ll_id.body));
-                self.maybe_typeck_results = Some(self.cx.tcx.typeck_body(ll_id.body));
-                let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value);
-
-                let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(rl_id.body));
-                self.maybe_typeck_results = Some(self.cx.tcx.typeck_body(rl_id.body));
-                let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value);
-
-                let eq_ty = self.eq_ty(lt, rt);
-                self.maybe_typeck_results = old_maybe_typeck_results;
-                eq_ty && ll == rl
-            },
-            (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
-                l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty)
-            },
-            (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => {
-                l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(&*l_rmut.ty, &*r_rmut.ty)
-            },
-            (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r),
-            (&TyKind::Tup(ref l), &TyKind::Tup(ref r)) => over(l, r, |l, r| self.eq_ty(l, r)),
-            (&TyKind::Infer, &TyKind::Infer) => true,
-            _ => false,
-        }
-    }
-
-    fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool {
-        left.ident.name == right.ident.name && self.eq_ty(&left.ty(), &right.ty())
-    }
-}
-
-/// Some simple reductions like `{ return }` => `return`
-fn reduce_exprkind<'hir>(kind: &'hir ExprKind<'hir>) -> &ExprKind<'hir> {
-    if let ExprKind::Block(block, _) = kind {
-        match (block.stmts, block.expr) {
-            // `{}` => `()`
-            ([], None) => &ExprKind::Tup(&[]),
-            ([], Some(expr)) => match expr.kind {
-                // `{ return .. }` => `return ..`
-                ExprKind::Ret(..) => &expr.kind,
-                _ => kind,
-            },
-            ([stmt], None) => match stmt.kind {
-                StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind {
-                    // `{ return ..; }` => `return ..`
-                    ExprKind::Ret(..) => &expr.kind,
-                    _ => kind,
-                },
-                _ => kind,
-            },
-            _ => kind,
-        }
-    } else {
-        kind
-    }
-}
-
-fn swap_binop<'a>(
-    binop: BinOpKind,
-    lhs: &'a Expr<'a>,
-    rhs: &'a Expr<'a>,
-) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
-    match binop {
-        BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => {
-            Some((binop, rhs, lhs))
-        },
-        BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
-        BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
-        BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
-        BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
-        BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698
-        | BinOpKind::Shl
-        | BinOpKind::Shr
-        | BinOpKind::Rem
-        | BinOpKind::Sub
-        | BinOpKind::Div
-        | BinOpKind::And
-        | BinOpKind::Or => None,
-    }
-}
-
-/// Checks if the two `Option`s are both `None` or some equal values as per
-/// `eq_fn`.
-pub fn both<X>(l: &Option<X>, r: &Option<X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
-    l.as_ref()
-        .map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
-}
-
-/// Checks if two slices are equal as per `eq_fn`.
-pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
-    left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
-}
-
-/// Checks if two expressions evaluate to the same value, and don't contain any side effects.
-pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
-    SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
-}
-
-/// Type used to hash an ast element. This is different from the `Hash` trait
-/// on ast types as this
-/// trait would consider IDs and spans.
-///
-/// All expressions kind are hashed, but some might have a weaker hash.
-pub struct SpanlessHash<'a, 'tcx> {
-    /// Context used to evaluate constant expressions.
-    cx: &'a LateContext<'tcx>,
-    maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
-    s: StableHasher,
-}
-
-impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
-    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
-        Self {
-            cx,
-            maybe_typeck_results: cx.maybe_typeck_results(),
-            s: StableHasher::new(),
-        }
-    }
-
-    pub fn finish(self) -> u64 {
-        self.s.finish()
-    }
-
-    pub fn hash_block(&mut self, b: &Block<'_>) {
-        for s in b.stmts {
-            self.hash_stmt(s);
-        }
-
-        if let Some(ref e) = b.expr {
-            self.hash_expr(e);
-        }
-
-        match b.rules {
-            BlockCheckMode::DefaultBlock => 0,
-            BlockCheckMode::UnsafeBlock(_) => 1,
-            BlockCheckMode::PushUnsafeBlock(_) => 2,
-            BlockCheckMode::PopUnsafeBlock(_) => 3,
-        }
-        .hash(&mut self.s);
-    }
-
-    #[allow(clippy::many_single_char_names, clippy::too_many_lines)]
-    pub fn hash_expr(&mut self, e: &Expr<'_>) {
-        let simple_const = self
-            .maybe_typeck_results
-            .and_then(|typeck_results| constant_simple(self.cx, typeck_results, e));
-
-        // const hashing may result in the same hash as some unrelated node, so add a sort of
-        // discriminant depending on which path we're choosing next
-        simple_const.is_some().hash(&mut self.s);
-
-        if let Some(e) = simple_const {
-            return e.hash(&mut self.s);
-        }
-
-        std::mem::discriminant(&e.kind).hash(&mut self.s);
-
-        match e.kind {
-            ExprKind::AddrOf(kind, m, ref e) => {
-                match kind {
-                    BorrowKind::Ref => 0,
-                    BorrowKind::Raw => 1,
-                }
-                .hash(&mut self.s);
-                m.hash(&mut self.s);
-                self.hash_expr(e);
-            },
-            ExprKind::Continue(i) => {
-                if let Some(i) = i.label {
-                    self.hash_name(i.ident.name);
-                }
-            },
-            ExprKind::Assign(ref l, ref r, _) => {
-                self.hash_expr(l);
-                self.hash_expr(r);
-            },
-            ExprKind::AssignOp(ref o, ref l, ref r) => {
-                o.node
-                    .hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
-                self.hash_expr(l);
-                self.hash_expr(r);
-            },
-            ExprKind::Block(ref b, _) => {
-                self.hash_block(b);
-            },
-            ExprKind::Binary(op, ref l, ref r) => {
-                op.node
-                    .hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
-                self.hash_expr(l);
-                self.hash_expr(r);
-            },
-            ExprKind::Break(i, ref j) => {
-                if let Some(i) = i.label {
-                    self.hash_name(i.ident.name);
-                }
-                if let Some(ref j) = *j {
-                    self.hash_expr(&*j);
-                }
-            },
-            ExprKind::Box(ref e) | ExprKind::DropTemps(ref e) | ExprKind::Yield(ref e, _) => {
-                self.hash_expr(e);
-            },
-            ExprKind::Call(ref fun, args) => {
-                self.hash_expr(fun);
-                self.hash_exprs(args);
-            },
-            ExprKind::Cast(ref e, ref ty) | ExprKind::Type(ref e, ref ty) => {
-                self.hash_expr(e);
-                self.hash_ty(ty);
-            },
-            ExprKind::Closure(cap, _, eid, _, _) => {
-                match cap {
-                    CaptureBy::Value => 0,
-                    CaptureBy::Ref => 1,
-                }
-                .hash(&mut self.s);
-                // closures inherit TypeckResults
-                self.hash_expr(&self.cx.tcx.hir().body(eid).value);
-            },
-            ExprKind::Field(ref e, ref f) => {
-                self.hash_expr(e);
-                self.hash_name(f.name);
-            },
-            ExprKind::Index(ref a, ref i) => {
-                self.hash_expr(a);
-                self.hash_expr(i);
-            },
-            ExprKind::InlineAsm(ref asm) => {
-                for piece in asm.template {
-                    match piece {
-                        InlineAsmTemplatePiece::String(s) => s.hash(&mut self.s),
-                        InlineAsmTemplatePiece::Placeholder {
-                            operand_idx,
-                            modifier,
-                            span: _,
-                        } => {
-                            operand_idx.hash(&mut self.s);
-                            modifier.hash(&mut self.s);
-                        },
-                    }
-                }
-                asm.options.hash(&mut self.s);
-                for (op, _op_sp) in asm.operands {
-                    match op {
-                        InlineAsmOperand::In { reg, expr } => {
-                            reg.hash(&mut self.s);
-                            self.hash_expr(expr);
-                        },
-                        InlineAsmOperand::Out { reg, late, expr } => {
-                            reg.hash(&mut self.s);
-                            late.hash(&mut self.s);
-                            if let Some(expr) = expr {
-                                self.hash_expr(expr);
-                            }
-                        },
-                        InlineAsmOperand::InOut { reg, late, expr } => {
-                            reg.hash(&mut self.s);
-                            late.hash(&mut self.s);
-                            self.hash_expr(expr);
-                        },
-                        InlineAsmOperand::SplitInOut {
-                            reg,
-                            late,
-                            in_expr,
-                            out_expr,
-                        } => {
-                            reg.hash(&mut self.s);
-                            late.hash(&mut self.s);
-                            self.hash_expr(in_expr);
-                            if let Some(out_expr) = out_expr {
-                                self.hash_expr(out_expr);
-                            }
-                        },
-                        InlineAsmOperand::Const { expr } | InlineAsmOperand::Sym { expr } => self.hash_expr(expr),
-                    }
-                }
-            },
-            ExprKind::LlvmInlineAsm(..) | ExprKind::Err => {},
-            ExprKind::Lit(ref l) => {
-                l.node.hash(&mut self.s);
-            },
-            ExprKind::Loop(ref b, ref i, ..) => {
-                self.hash_block(b);
-                if let Some(i) = *i {
-                    self.hash_name(i.ident.name);
-                }
-            },
-            ExprKind::If(ref cond, ref then, ref else_opt) => {
-                let c: fn(_, _, _) -> _ = ExprKind::If;
-                c.hash(&mut self.s);
-                self.hash_expr(cond);
-                self.hash_expr(&**then);
-                if let Some(ref e) = *else_opt {
-                    self.hash_expr(e);
-                }
-            },
-            ExprKind::Match(ref e, arms, ref s) => {
-                self.hash_expr(e);
-
-                for arm in arms {
-                    // TODO: arm.pat?
-                    if let Some(ref e) = arm.guard {
-                        self.hash_guard(e);
-                    }
-                    self.hash_expr(&arm.body);
-                }
-
-                s.hash(&mut self.s);
-            },
-            ExprKind::MethodCall(ref path, ref _tys, args, ref _fn_span) => {
-                self.hash_name(path.ident.name);
-                self.hash_exprs(args);
-            },
-            ExprKind::ConstBlock(ref l_id) => {
-                self.hash_body(l_id.body);
-            },
-            ExprKind::Repeat(ref e, ref l_id) => {
-                self.hash_expr(e);
-                self.hash_body(l_id.body);
-            },
-            ExprKind::Ret(ref e) => {
-                if let Some(ref e) = *e {
-                    self.hash_expr(e);
-                }
-            },
-            ExprKind::Path(ref qpath) => {
-                self.hash_qpath(qpath);
-            },
-            ExprKind::Struct(ref path, fields, ref expr) => {
-                self.hash_qpath(path);
-
-                for f in fields {
-                    self.hash_name(f.ident.name);
-                    self.hash_expr(&f.expr);
-                }
-
-                if let Some(ref e) = *expr {
-                    self.hash_expr(e);
-                }
-            },
-            ExprKind::Tup(tup) => {
-                self.hash_exprs(tup);
-            },
-            ExprKind::Array(v) => {
-                self.hash_exprs(v);
-            },
-            ExprKind::Unary(lop, ref le) => {
-                lop.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
-                self.hash_expr(le);
-            },
-        }
-    }
-
-    pub fn hash_exprs(&mut self, e: &[Expr<'_>]) {
-        for e in e {
-            self.hash_expr(e);
-        }
-    }
-
-    pub fn hash_name(&mut self, n: Symbol) {
-        n.as_str().hash(&mut self.s);
-    }
-
-    pub fn hash_qpath(&mut self, p: &QPath<'_>) {
-        match *p {
-            QPath::Resolved(_, ref path) => {
-                self.hash_path(path);
-            },
-            QPath::TypeRelative(_, ref path) => {
-                self.hash_name(path.ident.name);
-            },
-            QPath::LangItem(lang_item, ..) => {
-                lang_item.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
-            },
-        }
-        // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
-    }
-
-    pub fn hash_path(&mut self, p: &Path<'_>) {
-        p.is_global().hash(&mut self.s);
-        for p in p.segments {
-            self.hash_name(p.ident.name);
-        }
-    }
-
-    pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
-        std::mem::discriminant(&b.kind).hash(&mut self.s);
-
-        match &b.kind {
-            StmtKind::Local(local) => {
-                if let Some(ref init) = local.init {
-                    self.hash_expr(init);
-                }
-            },
-            StmtKind::Item(..) => {},
-            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
-                self.hash_expr(expr);
-            },
-        }
-    }
-
-    pub fn hash_guard(&mut self, g: &Guard<'_>) {
-        match g {
-            Guard::If(ref expr) | Guard::IfLet(_, ref expr) => {
-                self.hash_expr(expr);
-            },
-        }
-    }
-
-    pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
-        std::mem::discriminant(&lifetime.name).hash(&mut self.s);
-        if let LifetimeName::Param(ref name) = lifetime.name {
-            std::mem::discriminant(name).hash(&mut self.s);
-            match name {
-                ParamName::Plain(ref ident) => {
-                    ident.name.hash(&mut self.s);
-                },
-                ParamName::Fresh(ref size) => {
-                    size.hash(&mut self.s);
-                },
-                ParamName::Error => {},
-            }
-        }
-    }
-
-    pub fn hash_ty(&mut self, ty: &Ty<'_>) {
-        self.hash_tykind(&ty.kind);
-    }
-
-    pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
-        std::mem::discriminant(ty).hash(&mut self.s);
-        match ty {
-            TyKind::Slice(ty) => {
-                self.hash_ty(ty);
-            },
-            TyKind::Array(ty, anon_const) => {
-                self.hash_ty(ty);
-                self.hash_body(anon_const.body);
-            },
-            TyKind::Ptr(mut_ty) => {
-                self.hash_ty(&mut_ty.ty);
-                mut_ty.mutbl.hash(&mut self.s);
-            },
-            TyKind::Rptr(lifetime, mut_ty) => {
-                self.hash_lifetime(lifetime);
-                self.hash_ty(&mut_ty.ty);
-                mut_ty.mutbl.hash(&mut self.s);
-            },
-            TyKind::BareFn(bfn) => {
-                bfn.unsafety.hash(&mut self.s);
-                bfn.abi.hash(&mut self.s);
-                for arg in bfn.decl.inputs {
-                    self.hash_ty(&arg);
-                }
-                match bfn.decl.output {
-                    FnRetTy::DefaultReturn(_) => {
-                        ().hash(&mut self.s);
-                    },
-                    FnRetTy::Return(ref ty) => {
-                        self.hash_ty(ty);
-                    },
-                }
-                bfn.decl.c_variadic.hash(&mut self.s);
-            },
-            TyKind::Tup(ty_list) => {
-                for ty in *ty_list {
-                    self.hash_ty(ty);
-                }
-            },
-            TyKind::Path(qpath) => match qpath {
-                QPath::Resolved(ref maybe_ty, ref path) => {
-                    if let Some(ref ty) = maybe_ty {
-                        self.hash_ty(ty);
-                    }
-                    for segment in path.segments {
-                        segment.ident.name.hash(&mut self.s);
-                        self.hash_generic_args(segment.args().args);
-                    }
-                },
-                QPath::TypeRelative(ref ty, ref segment) => {
-                    self.hash_ty(ty);
-                    segment.ident.name.hash(&mut self.s);
-                },
-                QPath::LangItem(lang_item, ..) => {
-                    lang_item.hash(&mut self.s);
-                },
-            },
-            TyKind::OpaqueDef(_, arg_list) => {
-                self.hash_generic_args(arg_list);
-            },
-            TyKind::TraitObject(_, lifetime) => {
-                self.hash_lifetime(lifetime);
-            },
-            TyKind::Typeof(anon_const) => {
-                self.hash_body(anon_const.body);
-            },
-            TyKind::Err | TyKind::Infer | TyKind::Never => {},
-        }
-    }
-
-    pub fn hash_body(&mut self, body_id: BodyId) {
-        // swap out TypeckResults when hashing a body
-        let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body_id));
-        self.hash_expr(&self.cx.tcx.hir().body(body_id).value);
-        self.maybe_typeck_results = old_maybe_typeck_results;
-    }
-
-    fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
-        for arg in arg_list {
-            match arg {
-                GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
-                GenericArg::Type(ref ty) => self.hash_ty(&ty),
-                GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
-            }
-        }
-    }
-}
index d8c602fab22c2d095f94adeba358fe7c903e5bbc..0a347516c3ad2dd7e6e47ca04bd460d5c83e72c7 100644 (file)
@@ -4,7 +4,7 @@
     span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
 };
 use if_chain::if_chain;
-use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId};
+use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
 use rustc_ast::visit::FnKind;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::Applicability;
 
 impl EarlyLintPass for ClippyLintsInternal {
     fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) {
-        if let Some(utils) = krate
-            .module
-            .items
-            .iter()
-            .find(|item| item.ident.name.as_str() == "utils")
-        {
-            if let ItemKind::Mod(ref utils_mod) = utils.kind {
-                if let Some(paths) = utils_mod.items.iter().find(|item| item.ident.name.as_str() == "paths") {
-                    if let ItemKind::Mod(ref paths_mod) = paths.kind {
+        if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
+            if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
+                if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
+                    if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
                         let mut last_name: Option<SymbolStr> = None;
-                        for item in &*paths_mod.items {
+                        for item in items {
                             let name = item.ident.as_str();
                             if let Some(ref last_name) = last_name {
                                 if **last_name > *name {
@@ -343,7 +338,7 @@ pub struct LintWithoutLintPass {
 
 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if !run_lints(cx, &[DEFAULT_LINT], item.hir_id) {
+        if !run_lints(cx, &[DEFAULT_LINT], item.hir_id()) {
             return;
         }
 
@@ -393,7 +388,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
                         .find(|iiref| iiref.ident.as_str() == "get_lints")
                         .expect("LintPass needs to implement get_lints")
                         .id
-                        .hir_id,
+                        .hir_id(),
                 );
                 collector.visit_expr(&cx.tcx.hir().body(body_id).value);
             }
@@ -861,7 +856,7 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
 
 impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        let local_def_id = &cx.tcx.parent_module(item.hir_id);
+        let local_def_id = &cx.tcx.parent_module(item.hir_id());
         let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
         if_chain! {
             if mod_name.as_str() == "paths";
index fafa1400156a4f62db65495a56acd6a1db5ae65b..be9a07f8d7c9e10a900ef43d6d124720c6946d89 100644 (file)
-#[macro_use]
-pub mod sym_helper;
-
-#[allow(clippy::module_name_repetitions)]
-pub mod ast_utils;
-pub mod attrs;
 pub mod author;
-pub mod camel_case;
-pub mod comparisons;
 pub mod conf;
-mod diagnostics;
-pub mod eager_or_lazy;
-pub mod higher;
-mod hir_utils;
 pub mod inspector;
 #[cfg(feature = "internal-lints")]
 pub mod internal_lints;
-pub mod numeric_literal;
-pub mod paths;
-pub mod ptr;
-pub mod qualify_min_const_fn;
-pub mod sugg;
-pub mod usage;
-pub mod visitors;
-
-pub use self::attrs::*;
-pub use self::diagnostics::*;
-pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
-
-use std::borrow::Cow;
-use std::collections::hash_map::Entry;
-use std::hash::BuildHasherDefault;
-
-use if_chain::if_chain;
-use rustc_ast::ast::{self, Attribute, LitKind};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
-use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
-use rustc_hir::Node;
-use rustc_hir::{
-    def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind,
-    MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef, TyKind, Unsafety,
-};
-use rustc_infer::infer::TyCtxtInferExt;
-use rustc_lint::{LateContext, Level, Lint, LintContext};
-use rustc_middle::hir::exports::Export;
-use rustc_middle::hir::map::Map;
-use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable};
-use rustc_semver::RustcVersion;
-use rustc_session::Session;
-use rustc_span::hygiene::{ExpnKind, MacroKind};
-use rustc_span::source_map::original_sp;
-use rustc_span::sym;
-use rustc_span::symbol::{kw, Symbol};
-use rustc_span::{BytePos, Pos, Span, DUMMY_SP};
-use rustc_target::abi::Integer;
-use rustc_trait_selection::traits::query::normalize::AtExt;
-use smallvec::SmallVec;
-
-use crate::consts::{constant, Constant};
-
-pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
-    if let Ok(version) = RustcVersion::parse(msrv) {
-        return Some(version);
-    } else if let Some(sess) = sess {
-        if let Some(span) = span {
-            sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv));
-        }
-    }
-    None
-}
-
-pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool {
-    msrv.map_or(true, |msrv| msrv.meets(*lint_msrv))
-}
-
-macro_rules! extract_msrv_attr {
-    (LateContext) => {
-        extract_msrv_attr!(@LateContext, ());
-    };
-    (EarlyContext) => {
-        extract_msrv_attr!(@EarlyContext);
-    };
-    (@$context:ident$(, $call:tt)?) => {
-        fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'tcx>, attrs: &'tcx [rustc_ast::ast::Attribute]) {
-            use $crate::utils::get_unique_inner_attr;
-            match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") {
-                Some(msrv_attr) => {
-                    if let Some(msrv) = msrv_attr.value_str() {
-                        self.msrv = $crate::utils::parse_msrv(
-                            &msrv.to_string(),
-                            Some(cx.sess$($call)?),
-                            Some(msrv_attr.span),
-                        );
-                    } else {
-                        cx.sess$($call)?.span_err(msrv_attr.span, "bad clippy attribute");
-                    }
-                },
-                _ => (),
-            }
-        }
-    };
-}
-
-/// Returns `true` if the two spans come from differing expansions (i.e., one is
-/// from a macro and one isn't).
-#[must_use]
-pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
-    rhs.ctxt() != lhs.ctxt()
-}
-
-/// Returns `true` if the given `NodeId` is inside a constant context
-///
-/// # Example
-///
-/// ```rust,ignore
-/// if in_constant(cx, expr.hir_id) {
-///     // Do something
-/// }
-/// ```
-pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
-    let parent_id = cx.tcx.hir().get_parent_item(id);
-    match cx.tcx.hir().get(parent_id) {
-        Node::Item(&Item {
-            kind: ItemKind::Const(..) | ItemKind::Static(..),
-            ..
-        })
-        | Node::TraitItem(&TraitItem {
-            kind: TraitItemKind::Const(..),
-            ..
-        })
-        | Node::ImplItem(&ImplItem {
-            kind: ImplItemKind::Const(..),
-            ..
-        })
-        | Node::AnonConst(_) => true,
-        Node::Item(&Item {
-            kind: ItemKind::Fn(ref sig, ..),
-            ..
-        })
-        | Node::ImplItem(&ImplItem {
-            kind: ImplItemKind::Fn(ref sig, _),
-            ..
-        }) => sig.header.constness == Constness::Const,
-        _ => false,
-    }
-}
-
-/// Returns `true` if this `span` was expanded by any macro.
-#[must_use]
-pub fn in_macro(span: Span) -> bool {
-    if span.from_expansion() {
-        !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
-    } else {
-        false
-    }
-}
-
-// If the snippet is empty, it's an attribute that was inserted during macro
-// expansion and we want to ignore those, because they could come from external
-// sources that the user has no control over.
-// For some reason these attributes don't have any expansion info on them, so
-// we have to check it this way until there is a better way.
-pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
-    if let Some(snippet) = snippet_opt(cx, span) {
-        if snippet.is_empty() {
-            return false;
-        }
-    }
-    true
-}
-
-/// Checks if given pattern is a wildcard (`_`)
-pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool {
-    matches!(pat.kind, PatKind::Wild)
-}
-
-/// Checks if type is struct, enum or union type with the given def path.
-///
-/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
-/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
-pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
-    match ty.kind() {
-        ty::Adt(adt, _) => match_def_path(cx, adt.did, path),
-        _ => false,
-    }
-}
-
-/// Checks if the type is equal to a diagnostic item
-///
-/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
-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),
-        _ => false,
-    }
-}
-
-/// Checks if the type is equal to a lang item
-pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
-    match ty.kind() {
-        ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).unwrap() == adt.did,
-        _ => false,
-    }
-}
-
-/// Checks if the method call given in `expr` belongs to the given trait.
-pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
-    let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
-    let trt_id = cx.tcx.trait_of_item(def_id);
-    trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
-}
-
-/// Checks if an expression references a variable of the given name.
-pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool {
-    if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
-        if let [p] = path.segments {
-            return p.ident.name == var;
-        }
-    }
-    false
-}
-
-pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
-    match *path {
-        QPath::Resolved(_, ref path) => path.segments.last().expect("A path must have at least one segment"),
-        QPath::TypeRelative(_, ref seg) => seg,
-        QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
-    }
-}
-
-pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
-    match *path {
-        QPath::Resolved(_, ref path) => path.segments.get(0),
-        QPath::TypeRelative(_, ref seg) => Some(seg),
-        QPath::LangItem(..) => None,
-    }
-}
-
-/// Matches a `QPath` against a slice of segment string literals.
-///
-/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
-/// `rustc_hir::QPath`.
-///
-/// # Examples
-/// ```rust,ignore
-/// match_qpath(path, &["std", "rt", "begin_unwind"])
-/// ```
-pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
-    match *path {
-        QPath::Resolved(_, ref path) => match_path(path, segments),
-        QPath::TypeRelative(ref ty, ref segment) => match ty.kind {
-            TyKind::Path(ref inner_path) => {
-                if let [prefix @ .., end] = segments {
-                    if match_qpath(inner_path, prefix) {
-                        return segment.ident.name.as_str() == *end;
-                    }
-                }
-                false
-            },
-            _ => false,
-        },
-        QPath::LangItem(..) => false,
-    }
-}
-
-/// Matches a `Path` against a slice of segment string literals.
-///
-/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
-/// `rustc_hir::Path`.
-///
-/// # Examples
-///
-/// ```rust,ignore
-/// if match_path(&trait_ref.path, &paths::HASH) {
-///     // This is the `std::hash::Hash` trait.
-/// }
-///
-/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
-///     // This is a `rustc_middle::lint::Lint`.
-/// }
-/// ```
-pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
-    path.segments
-        .iter()
-        .rev()
-        .zip(segments.iter().rev())
-        .all(|(a, b)| a.ident.name.as_str() == *b)
-}
-
-/// Matches a `Path` against a slice of segment string literals, e.g.
-///
-/// # Examples
-/// ```rust,ignore
-/// match_path_ast(path, &["std", "rt", "begin_unwind"])
-/// ```
-pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
-    path.segments
-        .iter()
-        .rev()
-        .zip(segments.iter().rev())
-        .all(|(a, b)| a.ident.name.as_str() == *b)
-}
-
-/// If the expression is a path to a local, returns the canonical `HirId` of the local.
-pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
-    if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
-        if let Res::Local(id) = path.res {
-            return Some(id);
-        }
-    }
-    None
-}
-
-/// Returns true if the expression is a path to a local with the specified `HirId`.
-/// Use this function to see if an expression matches a function argument or a match binding.
-pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
-    path_to_local(expr) == Some(id)
-}
-
-/// Gets the definition associated to a path.
-#[allow(clippy::shadow_unrelated)] // false positive #6563
-pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
-    macro_rules! try_res {
-        ($e:expr) => {
-            match $e {
-                Some(e) => e,
-                None => return Res::Err,
-            }
-        };
-    }
-    fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export<HirId>> {
-        tcx.item_children(def_id)
-            .iter()
-            .find(|item| item.ident.name.as_str() == name)
-    }
-
-    let (krate, first, path) = match *path {
-        [krate, first, ref path @ ..] => (krate, first, path),
-        _ => return Res::Err,
-    };
-    let tcx = cx.tcx;
-    let crates = tcx.crates();
-    let krate = try_res!(crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate));
-    let first = try_res!(item_child_by_name(tcx, krate.as_def_id(), first));
-    let last = path
-        .iter()
-        .copied()
-        // `get_def_path` seems to generate these empty segments for extern blocks.
-        // We can just ignore them.
-        .filter(|segment| !segment.is_empty())
-        // for each segment, find the child item
-        .try_fold(first, |item, segment| {
-            let def_id = item.res.def_id();
-            if let Some(item) = item_child_by_name(tcx, def_id, segment) {
-                Some(item)
-            } else if matches!(item.res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
-                // it is not a child item so check inherent impl items
-                tcx.inherent_impls(def_id)
-                    .iter()
-                    .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment))
-            } else {
-                None
-            }
-        });
-    try_res!(last).res
-}
-
-/// Convenience function to get the `DefId` of a trait by path.
-/// It could be a trait or trait alias.
-pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
-    match path_to_res(cx, path) {
-        Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
-        _ => None,
-    }
-}
-
-/// Checks whether a type implements a trait.
-/// See also `get_trait_def_id`.
-pub fn implements_trait<'tcx>(
-    cx: &LateContext<'tcx>,
-    ty: Ty<'tcx>,
-    trait_id: DefId,
-    ty_params: &[GenericArg<'tcx>],
-) -> bool {
-    // Do not check on infer_types to avoid panic in evaluate_obligation.
-    if ty.has_infer_types() {
-        return false;
-    }
-    let ty = cx.tcx.erase_regions(ty);
-    if ty.has_escaping_bound_vars() {
-        return false;
-    }
-    let ty_params = cx.tcx.mk_substs(ty_params.iter());
-    cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env))
-}
-
-/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
-///
-/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
-///
-/// ```rust
-/// struct Point(isize, isize);
-///
-/// impl std::ops::Add for Point {
-///     type Output = Self;
-///
-///     fn add(self, other: Self) -> Self {
-///         Point(0, 0)
-///     }
-/// }
-/// ```
-pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx TraitRef<'tcx>> {
-    // Get the implemented trait for the current function
-    let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
-    if_chain! {
-        if parent_impl != hir::CRATE_HIR_ID;
-        if let hir::Node::Item(item) = cx.tcx.hir().get(parent_impl);
-        if let hir::ItemKind::Impl(impl_) = &item.kind;
-        then { return impl_.of_trait.as_ref(); }
-    }
-    None
-}
-
-/// Checks whether this type implements `Drop`.
-pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-    match ty.ty_adt_def() {
-        Some(def) => def.has_dtor(cx.tcx),
-        None => false,
-    }
-}
-
-/// Returns the method names and argument list of nested method call expressions that make up
-/// `expr`. method/span lists are sorted with the most recent call first.
-pub fn method_calls<'tcx>(
-    expr: &'tcx Expr<'tcx>,
-    max_depth: usize,
-) -> (Vec<Symbol>, Vec<&'tcx [Expr<'tcx>]>, Vec<Span>) {
-    let mut method_names = Vec::with_capacity(max_depth);
-    let mut arg_lists = Vec::with_capacity(max_depth);
-    let mut spans = Vec::with_capacity(max_depth);
-
-    let mut current = expr;
-    for _ in 0..max_depth {
-        if let ExprKind::MethodCall(path, span, args, _) = &current.kind {
-            if args.iter().any(|e| e.span.from_expansion()) {
-                break;
-            }
-            method_names.push(path.ident.name);
-            arg_lists.push(&**args);
-            spans.push(*span);
-            current = &args[0];
-        } else {
-            break;
-        }
-    }
-
-    (method_names, arg_lists, spans)
-}
-
-/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
-///
-/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
-/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
-/// containing the `Expr`s for
-/// `.bar()` and `.baz()`
-pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<&'a [Expr<'a>]>> {
-    let mut current = expr;
-    let mut matched = Vec::with_capacity(methods.len());
-    for method_name in methods.iter().rev() {
-        // method chains are stored last -> first
-        if let ExprKind::MethodCall(ref path, _, ref args, _) = current.kind {
-            if path.ident.name.as_str() == *method_name {
-                if args.iter().any(|e| e.span.from_expansion()) {
-                    return None;
-                }
-                matched.push(&**args); // build up `matched` backwards
-                current = &args[0] // go to parent expression
-            } else {
-                return None;
-            }
-        } else {
-            return None;
-        }
-    }
-    // Reverse `matched` so that it is in the same order as `methods`.
-    matched.reverse();
-    Some(matched)
-}
-
-/// Returns `true` if the provided `def_id` is an entrypoint to a program.
-pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
-    cx.tcx
-        .entry_fn(LOCAL_CRATE)
-        .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id.to_def_id())
-}
-
-/// Returns `true` if the expression is in the program's `#[panic_handler]`.
-pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
-    let parent = cx.tcx.hir().get_parent_item(e.hir_id);
-    let def_id = cx.tcx.hir().local_def_id(parent).to_def_id();
-    Some(def_id) == cx.tcx.lang_items().panic_impl()
-}
-
-/// Gets the name of the item the expression is in, if available.
-pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
-    let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
-    match cx.tcx.hir().find(parent_id) {
-        Some(
-            Node::Item(Item { ident, .. })
-            | Node::TraitItem(TraitItem { ident, .. })
-            | Node::ImplItem(ImplItem { ident, .. }),
-        ) => Some(ident.name),
-        _ => None,
-    }
-}
-
-/// Gets the name of a `Pat`, if any.
-pub fn get_pat_name(pat: &Pat<'_>) -> Option<Symbol> {
-    match pat.kind {
-        PatKind::Binding(.., ref spname, _) => Some(spname.name),
-        PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
-        PatKind::Box(ref p) | PatKind::Ref(ref p, _) => get_pat_name(&*p),
-        _ => None,
-    }
-}
-
-struct ContainsName {
-    name: Symbol,
-    result: bool,
-}
-
-impl<'tcx> Visitor<'tcx> for ContainsName {
-    type Map = Map<'tcx>;
-
-    fn visit_name(&mut self, _: Span, name: Symbol) {
-        if self.name == name {
-            self.result = true;
-        }
-    }
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
-}
-
-/// Checks if an `Expr` contains a certain name.
-pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
-    let mut cn = ContainsName { name, result: false };
-    cn.visit_expr(expr);
-    cn.result
-}
-
-/// 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;
-            }
-            if let hir::ExprKind::Ret(..) = &expr.kind {
-                self.found = true;
-            } else {
-                hir::intravisit::walk_expr(self, expr);
-            }
-        }
-
-        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
-    }
-}
-
-/// 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
-}
-
-/// Converts a span to a code snippet if available, otherwise use 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`.
-///
-/// # Example
-/// ```rust,ignore
-/// snippet(cx, expr.span, "..")
-/// ```
-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)
-}
-
-/// Same as `snippet`, but it adapts the applicability level by following rules:
-///
-/// - Applicability level `Unspecified` will never be changed.
-/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
-/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
-/// `HasPlaceholders`
-pub fn snippet_with_applicability<'a, T: LintContext>(
-    cx: &T,
-    span: Span,
-    default: &'a str,
-    applicability: &mut Applicability,
-) -> Cow<'a, str> {
-    if *applicability != Applicability::Unspecified && span.from_expansion() {
-        *applicability = Applicability::MaybeIncorrect;
-    }
-    snippet_opt(cx, span).map_or_else(
-        || {
-            if *applicability == Applicability::MachineApplicable {
-                *applicability = Applicability::HasPlaceholders;
-            }
-            Cow::Borrowed(default)
-        },
-        From::from,
-    )
-}
-
-/// Same as `snippet`, but should only be used when it's clear that the input span is
-/// not a macro argument.
-pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
-    snippet(cx, span.source_callsite(), default)
-}
-
-/// Converts a span to a code snippet. Returns `None` if not available.
-pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
-    cx.sess().source_map().span_to_snippet(span).ok()
-}
-
-/// Converts a span (from a block) to a code snippet if available, otherwise use default.
-///
-/// This trims the code of indentation, except for the first line. Use it for blocks or block-like
-/// things which need to be printed as such.
-///
-/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the
-/// resulting snippet of the given span.
-///
-/// # Example
-///
-/// ```rust,ignore
-/// snippet_block(cx, block.span, "..", None)
-/// // where, `block` is the block of the if expr
-///     if x {
-///         y;
-///     }
-/// // will return the snippet
-/// {
-///     y;
-/// }
-/// ```
-///
-/// ```rust,ignore
-/// snippet_block(cx, block.span, "..", Some(if_expr.span))
-/// // where, `block` is the block of the if expr
-///     if x {
-///         y;
-///     }
-/// // will return the snippet
-/// {
-///         y;
-///     } // aligned with `if`
-/// ```
-/// Note that the first line of the snippet always has 0 indentation.
-pub fn snippet_block<'a, T: LintContext>(
-    cx: &T,
-    span: Span,
-    default: &'a str,
-    indent_relative_to: Option<Span>,
-) -> Cow<'a, str> {
-    let snip = snippet(cx, span, default);
-    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
-    reindent_multiline(snip, true, indent)
-}
-
-/// Same as `snippet_block`, but adapts the applicability level by the rules of
-/// `snippet_with_applicability`.
-pub fn snippet_block_with_applicability<'a, T: LintContext>(
-    cx: &T,
-    span: Span,
-    default: &'a str,
-    indent_relative_to: Option<Span>,
-    applicability: &mut Applicability,
-) -> Cow<'a, str> {
-    let snip = snippet_with_applicability(cx, span, default, applicability);
-    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
-    reindent_multiline(snip, true, indent)
-}
-
-/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
-/// line.
-///
-/// ```rust,ignore
-///     let x = ();
-/// //          ^^
-/// // will be converted to
-///     let x = ();
-/// //  ^^^^^^^^^^
-/// ```
-pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
-    first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
-}
-
-fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
-    let line_span = line_span(cx, span);
-    snippet_opt(cx, line_span).and_then(|snip| {
-        snip.find(|c: char| !c.is_whitespace())
-            .map(|pos| line_span.lo() + BytePos::from_usize(pos))
-    })
-}
-
-/// Returns the indentation of the line of a span
-///
-/// ```rust,ignore
-/// let x = ();
-/// //      ^^ -- will return 0
-///     let x = ();
-/// //          ^^ -- will return 4
-/// ```
-pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
-    snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
-}
-
-/// Returns the positon just before rarrow
-///
-/// ```rust,ignore
-/// fn into(self) -> () {}
-///              ^
-/// // in case of unformatted code
-/// fn into2(self)-> () {}
-///               ^
-/// fn into3(self)   -> () {}
-///               ^
-/// ```
-pub fn position_before_rarrow(s: &str) -> Option<usize> {
-    s.rfind("->").map(|rpos| {
-        let mut rpos = rpos;
-        let chars: Vec<char> = s.chars().collect();
-        while rpos > 1 {
-            if let Some(c) = chars.get(rpos - 1) {
-                if c.is_whitespace() {
-                    rpos -= 1;
-                    continue;
-                }
-            }
-            break;
-        }
-        rpos
-    })
-}
-
-/// Extends the span to the beginning of the spans line, incl. whitespaces.
-///
-/// ```rust,ignore
-///        let x = ();
-/// //             ^^
-/// // will be converted to
-///        let x = ();
-/// // ^^^^^^^^^^^^^^
-/// ```
-fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
-    let span = original_sp(span, DUMMY_SP);
-    let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap();
-    let line_no = source_map_and_line.line;
-    let line_start = source_map_and_line.sf.lines[line_no];
-    Span::new(line_start, span.hi(), span.ctxt())
-}
-
-/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
-/// Also takes an `Option<String>` which can be put inside the braces.
-pub fn expr_block<'a, T: LintContext>(
-    cx: &T,
-    expr: &Expr<'_>,
-    option: Option<String>,
-    default: &'a str,
-    indent_relative_to: Option<Span>,
-) -> Cow<'a, str> {
-    let code = snippet_block(cx, expr.span, default, indent_relative_to);
-    let string = option.unwrap_or_default();
-    if expr.span.from_expansion() {
-        Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default)))
-    } else if let ExprKind::Block(_, _) = expr.kind {
-        Cow::Owned(format!("{}{}", code, string))
-    } else if string.is_empty() {
-        Cow::Owned(format!("{{ {} }}", code))
-    } else {
-        Cow::Owned(format!("{{\n{};\n{}\n}}", code, string))
-    }
-}
-
-/// Reindent a multiline string with possibility of ignoring the first line.
-#[allow(clippy::needless_pass_by_value)]
-pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
-    let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' ');
-    let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t');
-    reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into()
-}
-
-fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
-    let x = s
-        .lines()
-        .skip(ignore_first as usize)
-        .filter_map(|l| {
-            if l.is_empty() {
-                None
-            } else {
-                // ignore empty lines
-                Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0)
-            }
-        })
-        .min()
-        .unwrap_or(0);
-    let indent = indent.unwrap_or(0);
-    s.lines()
-        .enumerate()
-        .map(|(i, l)| {
-            if (ignore_first && i == 0) || l.is_empty() {
-                l.to_owned()
-            } else if x > indent {
-                l.split_at(x - indent).1.to_owned()
-            } else {
-                " ".repeat(indent - x) + l
-            }
-        })
-        .collect::<Vec<String>>()
-        .join("\n")
-}
-
-/// Gets the parent expression, if any –- this is useful to constrain a lint.
-pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-    let map = &cx.tcx.hir();
-    let hir_id = e.hir_id;
-    let parent_id = map.get_parent_node(hir_id);
-    if hir_id == parent_id {
-        return None;
-    }
-    map.find(parent_id).and_then(|node| {
-        if let Node::Expr(parent) = node {
-            Some(parent)
-        } else {
-            None
-        }
-    })
-}
-
-pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
-    let map = &cx.tcx.hir();
-    let enclosing_node = map
-        .get_enclosing_scope(hir_id)
-        .and_then(|enclosing_id| map.find(enclosing_id));
-    enclosing_node.and_then(|node| match node {
-        Node::Block(block) => Some(block),
-        Node::Item(&Item {
-            kind: ItemKind::Fn(_, _, eid),
-            ..
-        })
-        | Node::ImplItem(&ImplItem {
-            kind: ImplItemKind::Fn(_, eid),
-            ..
-        }) => match cx.tcx.hir().body(eid).value.kind {
-            ExprKind::Block(ref block, _) => Some(block),
-            _ => None,
-        },
-        _ => None,
-    })
-}
-
-/// Returns the base type for HIR references and pointers.
-pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
-    match ty.kind {
-        TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(&mut_ty.ty),
-        _ => ty,
-    }
-}
-
-/// Returns the base type for references and raw pointers, and count reference
-/// depth.
-pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
-    fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
-        match ty.kind() {
-            ty::Ref(_, ty, _) => inner(ty, depth + 1),
-            _ => (ty, depth),
-        }
-    }
-    inner(ty, 0)
-}
-
-/// Checks whether the given expression is a constant integer of the given value.
-/// unlike `is_integer_literal`, this version does const folding
-pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
-    if is_integer_literal(e, value) {
-        return true;
-    }
-    let map = cx.tcx.hir();
-    let parent_item = map.get_parent_item(e.hir_id);
-    if let Some((Constant::Int(v), _)) = map
-        .maybe_body_owned_by(parent_item)
-        .and_then(|body_id| constant(cx, cx.tcx.typeck_body(body_id), e))
-    {
-        value == v
-    } else {
-        false
-    }
-}
-
-/// Checks whether the given expression is a constant literal of the given value.
-pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
-    // FIXME: use constant folding
-    if let ExprKind::Lit(ref spanned) = expr.kind {
-        if let LitKind::Int(v, _) = spanned.node {
-            return v == value;
-        }
-    }
-    false
-}
-
-/// Returns `true` if the given `Expr` has been coerced before.
-///
-/// Examples of coercions can be found in the Nomicon at
-/// <https://doc.rust-lang.org/nomicon/coercions.html>.
-///
-/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_typeck::check::coercion` for more
-/// information on adjustments and coercions.
-pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
-    cx.typeck_results().adjustments().get(e.hir_id).is_some()
-}
-
-/// Returns the pre-expansion span if is this comes from an expansion of the
-/// macro `name`.
-/// See also `is_direct_expn_of`.
-#[must_use]
-pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
-    loop {
-        if span.from_expansion() {
-            let data = span.ctxt().outer_expn_data();
-            let new_span = data.call_site;
-
-            if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
-                if mac_name.as_str() == name {
-                    return Some(new_span);
-                }
-            }
-
-            span = new_span;
-        } else {
-            return None;
-        }
-    }
-}
-
-/// Returns the pre-expansion span if the span directly comes from an expansion
-/// of the macro `name`.
-/// The difference with `is_expn_of` is that in
-/// ```rust,ignore
-/// foo!(bar!(42));
-/// ```
-/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
-/// `bar!` by
-/// `is_direct_expn_of`.
-#[must_use]
-pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
-    if span.from_expansion() {
-        let data = span.ctxt().outer_expn_data();
-        let new_span = data.call_site;
-
-        if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
-            if mac_name.as_str() == name {
-                return Some(new_span);
-            }
-        }
-    }
-
-    None
-}
-
-/// Convenience function to get the return type of a function.
-pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
-    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
-    let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
-    cx.tcx.erase_late_bound_regions(ret_ty)
-}
-
-/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
-pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool {
-    ty.walk().any(|inner| match inner.unpack() {
-        GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty),
-        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
-    })
-}
-
-/// Returns `true` if the given type is an `unsafe` function.
-pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-    match ty.kind() {
-        ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
-        _ => false,
-    }
-}
-
-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)
-}
-
-/// Checks if an expression is constructing a tuple-like enum variant or struct
-pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    if let ExprKind::Call(ref fun, _) = expr.kind {
-        if let ExprKind::Path(ref qp) = fun.kind {
-            let res = cx.qpath_res(qp, fun.hir_id);
-            return match res {
-                def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
-                def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
-                _ => false,
-            };
-        }
-    }
-    false
-}
-
-/// Returns `true` if a pattern is refutable.
-// TODO: should be implemented using rustc/mir_build/thir machinery
-pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
-    fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
-        matches!(
-            cx.qpath_res(qpath, id),
-            def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
-        )
-    }
-
-    fn are_refutable<'a, I: Iterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, mut i: I) -> bool {
-        i.any(|pat| is_refutable(cx, pat))
-    }
-
-    match pat.kind {
-        PatKind::Wild => false,
-        PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
-        PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => is_refutable(cx, pat),
-        PatKind::Lit(..) | PatKind::Range(..) => true,
-        PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
-        PatKind::Or(ref pats) => {
-            // TODO: should be the honest check, that pats is exhaustive set
-            are_refutable(cx, pats.iter().map(|pat| &**pat))
-        },
-        PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
-        PatKind::Struct(ref qpath, ref fields, _) => {
-            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat))
-        },
-        PatKind::TupleStruct(ref qpath, ref pats, _) => {
-            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat))
-        },
-        PatKind::Slice(ref head, ref middle, ref tail) => {
-            match &cx.typeck_results().node_type(pat.hir_id).kind() {
-                ty::Slice(..) => {
-                    // [..] is the only irrefutable slice pattern.
-                    !head.is_empty() || middle.is_none() || !tail.is_empty()
-                },
-                ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)),
-                _ => {
-                    // unreachable!()
-                    true
-                },
-            }
-        },
-    }
-}
-
-/// 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))
-}
-
-/// Remove blocks around an expression.
-///
-/// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return
-/// themselves.
-pub fn remove_blocks<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
-    while let ExprKind::Block(ref block, ..) = expr.kind {
-        match (block.stmts.is_empty(), block.expr.as_ref()) {
-            (true, Some(e)) => expr = e,
-            _ => break,
-        }
-    }
-    expr
-}
-
-pub fn is_self(slf: &Param<'_>) -> bool {
-    if let PatKind::Binding(.., name, _) = slf.pat.kind {
-        name.name == kw::SelfLower
-    } else {
-        false
-    }
-}
-
-pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
-    if_chain! {
-        if let TyKind::Path(QPath::Resolved(None, ref path)) = slf.kind;
-        if let Res::SelfTy(..) = path.res;
-        then {
-            return true
-        }
-    }
-    false
-}
-
-pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
-    (0..decl.inputs.len()).map(move |i| &body.params[i])
-}
-
-/// Checks if a given expression is a match expression expanded from the `?`
-/// operator or the `try` macro.
-pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
-    fn is_ok(arm: &Arm<'_>) -> bool {
-        if_chain! {
-            if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pat.kind;
-            if match_qpath(path, &paths::RESULT_OK[1..]);
-            if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
-            if path_to_local_id(arm.body, hir_id);
-            then {
-                return true;
-            }
-        }
-        false
-    }
-
-    fn is_err(arm: &Arm<'_>) -> bool {
-        if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
-            match_qpath(path, &paths::RESULT_ERR[1..])
-        } else {
-            false
-        }
-    }
-
-    if let ExprKind::Match(_, ref arms, ref source) = expr.kind {
-        // desugared from a `?` operator
-        if let MatchSource::TryDesugar = *source {
-            return Some(expr);
-        }
-
-        if_chain! {
-            if arms.len() == 2;
-            if arms[0].guard.is_none();
-            if arms[1].guard.is_none();
-            if (is_ok(&arms[0]) && is_err(&arms[1])) ||
-                (is_ok(&arms[1]) && is_err(&arms[0]));
-            then {
-                return Some(expr);
-            }
-        }
-    }
-
-    None
-}
-
-/// Returns `true` if the lint is allowed in the current context
-///
-/// Useful for skipping long running code when it's unnecessary
-pub fn is_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
-    cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
-}
-
-pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
-    while let PatKind::Ref(subpat, _) = pat.kind {
-        pat = subpat;
-    }
-    pat
-}
-
-pub fn int_bits(tcx: TyCtxt<'_>, ity: ty::IntTy) -> u64 {
-    Integer::from_int_ty(&tcx, ity).size().bits()
-}
-
-#[allow(clippy::cast_possible_wrap)]
-/// Turn a constant int byte representation into an i128
-pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: ty::IntTy) -> i128 {
-    let amt = 128 - int_bits(tcx, ity);
-    ((u as i128) << amt) >> amt
-}
-
-#[allow(clippy::cast_sign_loss)]
-/// clip unused bytes
-pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: ty::IntTy) -> u128 {
-    let amt = 128 - int_bits(tcx, ity);
-    ((u as u128) << amt) >> amt
-}
-
-/// clip unused bytes
-pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: ty::UintTy) -> u128 {
-    let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
-    let amt = 128 - bits;
-    (u << amt) >> amt
-}
-
-/// Removes block comments from the given `Vec` of lines.
-///
-/// # Examples
-///
-/// ```rust,ignore
-/// without_block_comments(vec!["/*", "foo", "*/"]);
-/// // => vec![]
-///
-/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
-/// // => vec!["bar"]
-/// ```
-pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
-    let mut without = vec![];
-
-    let mut nest_level = 0;
-
-    for line in lines {
-        if line.contains("/*") {
-            nest_level += 1;
-            continue;
-        } else if line.contains("*/") {
-            nest_level -= 1;
-            continue;
-        }
-
-        if nest_level == 0 {
-            without.push(line);
-        }
-    }
-
-    without
-}
-
-pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> 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)) {
-            return true;
-        }
-        prev_enclosing_node = Some(enclosing_node);
-        enclosing_node = map.get_parent_item(enclosing_node);
-    }
-    false
-}
-
-/// Returns true if ty has `iter` or `iter_mut` methods
-pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<&'static str> {
-    // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
-    // exists and has the desired signature. Unfortunately FnCtxt is not exported
-    // so we can't use its `lookup_method` method.
-    let into_iter_collections: [&[&str]; 13] = [
-        &paths::VEC,
-        &paths::OPTION,
-        &paths::RESULT,
-        &paths::BTREESET,
-        &paths::BTREEMAP,
-        &paths::VEC_DEQUE,
-        &paths::LINKED_LIST,
-        &paths::BINARY_HEAP,
-        &paths::HASHSET,
-        &paths::HASHMAP,
-        &paths::PATH_BUF,
-        &paths::PATH,
-        &paths::RECEIVER,
-    ];
-
-    let ty_to_check = match probably_ref_ty.kind() {
-        ty::Ref(_, ty_to_check, _) => ty_to_check,
-        _ => probably_ref_ty,
-    };
-
-    let def_id = match ty_to_check.kind() {
-        ty::Array(..) => return Some("array"),
-        ty::Slice(..) => return Some("slice"),
-        ty::Adt(adt, _) => adt.did,
-        _ => return None,
-    };
-
-    for path in &into_iter_collections {
-        if match_def_path(cx, def_id, path) {
-            return Some(*path.last().unwrap());
-        }
-    }
-    None
-}
-
-/// Matches a function call with the given path and returns the arguments.
-///
-/// Usage:
-///
-/// ```rust,ignore
-/// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
-/// ```
-pub fn match_function_call<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx Expr<'_>,
-    path: &[&str],
-) -> Option<&'tcx [Expr<'tcx>]> {
-    if_chain! {
-        if let ExprKind::Call(ref fun, ref args) = expr.kind;
-        if let ExprKind::Path(ref qpath) = fun.kind;
-        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
-        if match_def_path(cx, fun_def_id, path);
-        then {
-            return Some(&args)
-        }
-    };
-    None
-}
-
-/// Checks if `Ty` is normalizable. This function is useful
-/// to avoid crashes on `layout_of`.
-pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
-    cx.tcx.infer_ctxt().enter(|infcx| {
-        let cause = rustc_middle::traits::ObligationCause::dummy();
-        infcx.at(&cause, param_env).normalize(ty).is_ok()
-    })
-}
-
-pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
-    // We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path`
-    // accepts only that. We should probably move to Symbols in Clippy as well.
-    let syms = syms.iter().map(|p| Symbol::intern(p)).collect::<Vec<Symbol>>();
-    cx.match_def_path(did, &syms)
-}
-
-pub fn match_panic_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx [Expr<'tcx>]> {
-    match_function_call(cx, expr, &paths::BEGIN_PANIC)
-        .or_else(|| match_function_call(cx, expr, &paths::BEGIN_PANIC_FMT))
-        .or_else(|| match_function_call(cx, expr, &paths::PANIC_ANY))
-        .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC))
-        .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_FMT))
-        .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_STR))
-}
-
-pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool {
-    match_def_path(cx, did, &paths::BEGIN_PANIC)
-        || match_def_path(cx, did, &paths::BEGIN_PANIC_FMT)
-        || match_def_path(cx, did, &paths::PANIC_ANY)
-        || match_def_path(cx, did, &paths::PANICKING_PANIC)
-        || match_def_path(cx, did, &paths::PANICKING_PANIC_FMT)
-        || match_def_path(cx, did, &paths::PANICKING_PANIC_STR)
-}
-
-/// Returns the list of condition expressions and the list of blocks in a
-/// sequence of `if/else`.
-/// E.g., this returns `([a, b], [c, d, e])` for the expression
-/// `if a { c } else if b { d } else { e }`.
-pub fn if_sequence<'tcx>(
-    mut expr: &'tcx Expr<'tcx>,
-) -> (SmallVec<[&'tcx Expr<'tcx>; 1]>, SmallVec<[&'tcx Block<'tcx>; 1]>) {
-    let mut conds = SmallVec::new();
-    let mut blocks: SmallVec<[&Block<'_>; 1]> = SmallVec::new();
-
-    while let ExprKind::If(ref cond, ref then_expr, ref else_expr) = expr.kind {
-        conds.push(&**cond);
-        if let ExprKind::Block(ref block, _) = then_expr.kind {
-            blocks.push(block);
-        } else {
-            panic!("ExprKind::If node is not an ExprKind::Block");
-        }
-
-        if let Some(ref else_expr) = *else_expr {
-            expr = else_expr;
-        } else {
-            break;
-        }
-    }
-
-    // final `else {..}`
-    if !blocks.is_empty() {
-        if let ExprKind::Block(ref block, _) = expr.kind {
-            blocks.push(&**block);
-        }
-    }
-
-    (conds, blocks)
-}
-
-pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
-    let map = cx.tcx.hir();
-    let parent_id = map.get_parent_node(expr.hir_id);
-    let parent_node = map.get(parent_id);
-    matches!(
-        parent_node,
-        Node::Expr(Expr {
-            kind: ExprKind::If(_, _, _),
-            ..
-        })
-    )
-}
-
-// Finds the attribute with the given name, if any
-pub fn attr_by_name<'a>(attrs: &'a [Attribute], name: &'_ str) -> Option<&'a Attribute> {
-    attrs
-        .iter()
-        .find(|attr| attr.ident().map_or(false, |ident| ident.as_str() == name))
-}
-
-// Finds the `#[must_use]` attribute, if any
-pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
-    attr_by_name(attrs, "must_use")
-}
-
-// Returns whether the type has #[must_use] attribute
-pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-    match ty.kind() {
-        ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(),
-        ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(),
-        ty::Slice(ref ty)
-        | ty::Array(ref ty, _)
-        | ty::RawPtr(ty::TypeAndMut { ref ty, .. })
-        | ty::Ref(_, ref ty, _) => {
-            // for the Array case we don't need to care for the len == 0 case
-            // because we don't want to lint functions returning empty arrays
-            is_must_use_ty(cx, *ty)
-        },
-        ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)),
-        ty::Opaque(ref def_id, _) => {
-            for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
-                if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() {
-                    if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() {
-                        return true;
-                    }
-                }
-            }
-            false
-        },
-        ty::Dynamic(binder, _) => {
-            for predicate in binder.iter() {
-                if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
-                    if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
-                        return true;
-                    }
-                }
-            }
-            false
-        },
-        _ => false,
-    }
-}
-
-// check if expr is calling method or function with #[must_use] attribute
-pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    let did = match expr.kind {
-        ExprKind::Call(ref path, _) => if_chain! {
-            if let ExprKind::Path(ref qpath) = path.kind;
-            if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
-            then {
-                Some(did)
-            } else {
-                None
-            }
-        },
-        ExprKind::MethodCall(_, _, _, _) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
-        _ => None,
-    };
-
-    did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some())
-}
-
-pub fn is_no_std_crate(krate: &Crate<'_>) -> bool {
-    krate.item.attrs.iter().any(|attr| {
-        if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
-            attr.path == sym::no_std
-        } else {
-            false
-        }
-    })
-}
-
-/// Check if parent of a hir node is a trait implementation block.
-/// For example, `f` in
-/// ```rust,ignore
-/// impl Trait for S {
-///     fn f() {}
-/// }
-/// ```
-pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
-    if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
-        matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
-    } else {
-        false
-    }
-}
-
-/// Check if it's even possible to satisfy the `where` clause for the item.
-///
-/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
-///
-/// ```ignore
-/// fn foo() where i32: Iterator {
-///     for _ in 2i32 {}
-/// }
-/// ```
-pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
-    use rustc_trait_selection::traits;
-    let predicates =
-        cx.tcx
-            .predicates_of(did)
-            .predicates
-            .iter()
-            .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
-    traits::impossible_predicates(
-        cx.tcx,
-        traits::elaborate_predicates(cx.tcx, predicates)
-            .map(|o| o.predicate)
-            .collect::<Vec<_>>(),
-    )
-}
-
-/// Returns the `DefId` of the callee if the given expression is a function or method call.
-pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
-    match &expr.kind {
-        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
-        ExprKind::Call(
-            Expr {
-                kind: ExprKind::Path(qpath),
-                hir_id: path_hir_id,
-                ..
-            },
-            ..,
-        ) => cx.typeck_results().qpath_res(qpath, *path_hir_id).opt_def_id(),
-        _ => None,
-    }
-}
-
-pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool {
-    lints.iter().any(|lint| {
-        matches!(
-            cx.tcx.lint_level_at_node(lint, id),
-            (Level::Forbid | Level::Deny | Level::Warn, _)
-        )
-    })
-}
-
-/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point
-/// number type, a str, or an array, slice, or tuple of those types).
-pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
-    match ty.kind() {
-        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
-        ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
-        ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
-        ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type),
-        _ => false,
-    }
-}
-
-/// Returns Option<String> where String is a textual representation of the type encapsulated in the
-/// slice iff the given expression is a slice of primitives (as defined in the
-/// `is_recursively_primitive_type` function) and None otherwise.
-pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
-    let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
-    let expr_kind = expr_type.kind();
-    let is_primitive = match expr_kind {
-        ty::Slice(element_type) => is_recursively_primitive_type(element_type),
-        ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &ty::Slice(_)) => {
-            if let ty::Slice(element_type) = inner_ty.kind() {
-                is_recursively_primitive_type(element_type)
-            } else {
-                unreachable!()
-            }
-        },
-        _ => false,
-    };
-
-    if is_primitive {
-        // if we have wrappers like Array, Slice or Tuple, print these
-        // and get the type enclosed in the slice ref
-        match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
-            ty::Slice(..) => return Some("slice".into()),
-            ty::Array(..) => return Some("array".into()),
-            ty::Tuple(..) => return Some("tuple".into()),
-            _ => {
-                // is_recursively_primitive_type() should have taken care
-                // of the rest and we can rely on the type that is found
-                let refs_peeled = expr_type.peel_refs();
-                return Some(refs_peeled.walk().last().unwrap().to_string());
-            },
-        }
-    }
-    None
-}
-
-/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
-/// `hash` must be comformed with `eq`
-pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
-where
-    Hash: Fn(&T) -> u64,
-    Eq: Fn(&T, &T) -> bool,
-{
-    if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) {
-        return vec![(&exprs[0], &exprs[1])];
-    }
-
-    let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
-
-    let mut map: FxHashMap<_, Vec<&_>> =
-        FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
-
-    for expr in exprs {
-        match map.entry(hash(expr)) {
-            Entry::Occupied(mut o) => {
-                for o in o.get() {
-                    if eq(o, expr) {
-                        match_expr_list.push((o, expr));
-                    }
-                }
-                o.get_mut().push(expr);
-            },
-            Entry::Vacant(v) => {
-                v.insert(vec![expr]);
-            },
-        }
-    }
-
-    match_expr_list
-}
-
-/// Peels off all references on the pattern. Returns the underlying pattern and the number of
-/// references removed.
-pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
-    fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
-        if let PatKind::Ref(pat, _) = pat.kind {
-            peel(pat, count + 1)
-        } else {
-            (pat, count)
-        }
-    }
-    peel(pat, 0)
-}
-
-/// Peels off up to the given number of references on the expression. Returns the underlying
-/// expression and the number of references removed.
-pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
-    fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) {
-        match expr.kind {
-            ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target),
-            _ => (expr, count),
-        }
-    }
-    f(expr, 0, count)
-}
-
-/// Peels off all references on the type. Returns the underlying type and the number of references
-/// removed.
-pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
-    fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
-        if let ty::Ref(_, ty, _) = ty.kind() {
-            peel(ty, count + 1)
-        } else {
-            (ty, count)
-        }
-    }
-    peel(ty, 0)
-}
-
-#[macro_export]
-macro_rules! unwrap_cargo_metadata {
-    ($cx: ident, $lint: ident, $deps: expr) => {{
-        let mut command = cargo_metadata::MetadataCommand::new();
-        if !$deps {
-            command.no_deps();
-        }
-
-        match command.exec() {
-            Ok(metadata) => metadata,
-            Err(err) => {
-                span_lint($cx, $lint, DUMMY_SP, &format!("could not read cargo metadata: {}", err));
-                return;
-            },
-        }
-    }};
-}
-
-pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
-    if_chain! {
-        if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
-        if let Res::Def(_, def_id) = path.res;
-        then {
-            cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr)
-        } else {
-            false
-        }
-    }
-}
-
-/// Check if the resolution of a given path is an `Ok` variant of `Result`.
-pub fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool {
-    if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() {
-        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
-            if let Some(variant_id) = cx.tcx.parent(id) {
-                return variant_id == ok_id;
-            }
-        }
-    }
-    false
-}
-
-/// Check if the resolution of a given path is a `Some` variant of `Option`.
-pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool {
-    if let Some(some_id) = cx.tcx.lang_items().option_some_variant() {
-        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
-            if let Some(variant_id) = cx.tcx.parent(id) {
-                return variant_id == some_id;
-            }
-        }
-    }
-    false
-}
-
-#[cfg(test)]
-mod test {
-    use super::{reindent_multiline, without_block_comments};
-
-    #[test]
-    fn test_reindent_multiline_single_line() {
-        assert_eq!("", reindent_multiline("".into(), false, None));
-        assert_eq!("...", reindent_multiline("...".into(), false, None));
-        assert_eq!("...", reindent_multiline("    ...".into(), false, None));
-        assert_eq!("...", reindent_multiline("\t...".into(), false, None));
-        assert_eq!("...", reindent_multiline("\t\t...".into(), false, None));
-    }
-
-    #[test]
-    #[rustfmt::skip]
-    fn test_reindent_multiline_block() {
-        assert_eq!("\
-    if x {
-        y
-    } else {
-        z
-    }", reindent_multiline("    if x {
-            y
-        } else {
-            z
-        }".into(), false, None));
-        assert_eq!("\
-    if x {
-    \ty
-    } else {
-    \tz
-    }", reindent_multiline("    if x {
-        \ty
-        } else {
-        \tz
-        }".into(), false, None));
-    }
-
-    #[test]
-    #[rustfmt::skip]
-    fn test_reindent_multiline_empty_line() {
-        assert_eq!("\
-    if x {
-        y
-
-    } else {
-        z
-    }", reindent_multiline("    if x {
-            y
-
-        } else {
-            z
-        }".into(), false, None));
-    }
-
-    #[test]
-    #[rustfmt::skip]
-    fn test_reindent_multiline_lines_deeper() {
-        assert_eq!("\
-        if x {
-            y
-        } else {
-            z
-        }", reindent_multiline("\
-    if x {
-        y
-    } else {
-        z
-    }".into(), true, Some(8)));
-    }
-
-    #[test]
-    fn test_without_block_comments_lines_without_block_comments() {
-        let result = without_block_comments(vec!["/*", "", "*/"]);
-        println!("result: {:?}", result);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
-        assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
-
-        let result = without_block_comments(vec!["/* rust", "", "*/"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["/* one-line comment */"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
-        assert!(result.is_empty());
 
-        let result = without_block_comments(vec!["foo", "bar", "baz"]);
-        assert_eq!(result, vec!["foo", "bar", "baz"]);
-    }
-}
+pub use clippy_utils::*;
diff --git a/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs b/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs
deleted file mode 100644 (file)
index d02603d..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
-
-#[derive(Debug, PartialEq, Copy, Clone)]
-pub enum Radix {
-    Binary,
-    Octal,
-    Decimal,
-    Hexadecimal,
-}
-
-impl Radix {
-    /// Returns a reasonable digit group size for this radix.
-    #[must_use]
-    fn suggest_grouping(self) -> usize {
-        match self {
-            Self::Binary | Self::Hexadecimal => 4,
-            Self::Octal | Self::Decimal => 3,
-        }
-    }
-}
-
-/// A helper method to format numeric literals with digit grouping.
-/// `lit` must be a valid numeric literal without suffix.
-pub fn format(lit: &str, type_suffix: Option<&str>, float: bool) -> String {
-    NumericLiteral::new(lit, type_suffix, float).format()
-}
-
-#[derive(Debug)]
-pub struct NumericLiteral<'a> {
-    /// Which radix the literal was represented in.
-    pub radix: Radix,
-    /// The radix prefix, if present.
-    pub prefix: Option<&'a str>,
-
-    /// The integer part of the number.
-    pub integer: &'a str,
-    /// The fraction part of the number.
-    pub fraction: Option<&'a str>,
-    /// The exponent separator (b'e' or b'E') including preceding underscore if present
-    /// and the exponent part.
-    pub exponent: Option<(&'a str, &'a str)>,
-
-    /// The type suffix, including preceding underscore if present.
-    pub suffix: Option<&'a str>,
-}
-
-impl<'a> NumericLiteral<'a> {
-    pub fn from_lit(src: &'a str, lit: &Lit) -> Option<NumericLiteral<'a>> {
-        NumericLiteral::from_lit_kind(src, &lit.kind)
-    }
-
-    pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
-        if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) {
-            let (unsuffixed, suffix) = split_suffix(&src, lit_kind);
-            let float = matches!(lit_kind, LitKind::Float(..));
-            Some(NumericLiteral::new(unsuffixed, suffix, float))
-        } else {
-            None
-        }
-    }
-
-    #[must_use]
-    pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self {
-        // Determine delimiter for radix prefix, if present, and radix.
-        let radix = if lit.starts_with("0x") {
-            Radix::Hexadecimal
-        } else if lit.starts_with("0b") {
-            Radix::Binary
-        } else if lit.starts_with("0o") {
-            Radix::Octal
-        } else {
-            Radix::Decimal
-        };
-
-        // Grab part of the literal after prefix, if present.
-        let (prefix, mut sans_prefix) = if let Radix::Decimal = radix {
-            (None, lit)
-        } else {
-            let (p, s) = lit.split_at(2);
-            (Some(p), s)
-        };
-
-        if suffix.is_some() && sans_prefix.ends_with('_') {
-            // The '_' before the suffix isn't part of the digits
-            sans_prefix = &sans_prefix[..sans_prefix.len() - 1];
-        }
-
-        let (integer, fraction, exponent) = Self::split_digit_parts(sans_prefix, float);
-
-        Self {
-            radix,
-            prefix,
-            integer,
-            fraction,
-            exponent,
-            suffix,
-        }
-    }
-
-    pub fn is_decimal(&self) -> bool {
-        self.radix == Radix::Decimal
-    }
-
-    pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) {
-        let mut integer = digits;
-        let mut fraction = None;
-        let mut exponent = None;
-
-        if float {
-            for (i, c) in digits.char_indices() {
-                match c {
-                    '.' => {
-                        integer = &digits[..i];
-                        fraction = Some(&digits[i + 1..]);
-                    },
-                    'e' | 'E' => {
-                        let exp_start = if digits[..i].ends_with('_') { i - 1 } else { i };
-
-                        if integer.len() > exp_start {
-                            integer = &digits[..exp_start];
-                        } else {
-                            fraction = Some(&digits[integer.len() + 1..exp_start]);
-                        };
-                        exponent = Some((&digits[exp_start..=i], &digits[i + 1..]));
-                        break;
-                    },
-                    _ => {},
-                }
-            }
-        }
-
-        (integer, fraction, exponent)
-    }
-
-    /// Returns literal formatted in a sensible way.
-    pub fn format(&self) -> String {
-        let mut output = String::new();
-
-        if let Some(prefix) = self.prefix {
-            output.push_str(prefix);
-        }
-
-        let group_size = self.radix.suggest_grouping();
-
-        Self::group_digits(
-            &mut output,
-            self.integer,
-            group_size,
-            true,
-            self.radix == Radix::Hexadecimal,
-        );
-
-        if let Some(fraction) = self.fraction {
-            output.push('.');
-            Self::group_digits(&mut output, fraction, group_size, false, false);
-        }
-
-        if let Some((separator, exponent)) = self.exponent {
-            output.push_str(separator);
-            Self::group_digits(&mut output, exponent, group_size, true, false);
-        }
-
-        if let Some(suffix) = self.suffix {
-            output.push('_');
-            output.push_str(suffix);
-        }
-
-        output
-    }
-
-    pub fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, pad: bool) {
-        debug_assert!(group_size > 0);
-
-        let mut digits = input.chars().filter(|&c| c != '_');
-
-        let first_group_size;
-
-        if partial_group_first {
-            first_group_size = (digits.clone().count() - 1) % group_size + 1;
-            if pad {
-                for _ in 0..group_size - first_group_size {
-                    output.push('0');
-                }
-            }
-        } else {
-            first_group_size = group_size;
-        }
-
-        for _ in 0..first_group_size {
-            if let Some(digit) = digits.next() {
-                output.push(digit);
-            }
-        }
-
-        for (c, i) in digits.zip((0..group_size).cycle()) {
-            if i == 0 {
-                output.push('_');
-            }
-            output.push(c);
-        }
-    }
-}
-
-fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) {
-    debug_assert!(lit_kind.is_numeric());
-    lit_suffix_length(lit_kind).map_or((src, None), |suffix_length| {
-        let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length);
-        (unsuffixed, Some(suffix))
-    })
-}
-
-fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> {
-    debug_assert!(lit_kind.is_numeric());
-    let suffix = match lit_kind {
-        LitKind::Int(_, int_lit_kind) => match int_lit_kind {
-            LitIntType::Signed(int_ty) => Some(int_ty.name_str()),
-            LitIntType::Unsigned(uint_ty) => Some(uint_ty.name_str()),
-            LitIntType::Unsuffixed => None,
-        },
-        LitKind::Float(_, float_lit_kind) => match float_lit_kind {
-            LitFloatType::Suffixed(float_ty) => Some(float_ty.name_str()),
-            LitFloatType::Unsuffixed => None,
-        },
-        _ => None,
-    };
-
-    suffix.map(str::len)
-}
diff --git a/src/tools/clippy/clippy_lints/src/utils/paths.rs b/src/tools/clippy/clippy_lints/src/utils/paths.rs
deleted file mode 100644 (file)
index e617867..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-//! This module contains paths to types and functions Clippy needs to know
-//! about.
-//!
-//! Whenever possible, please consider diagnostic items over hardcoded paths.
-//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
-
-pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
-pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
-pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
-pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
-pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
-pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"];
-pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
-pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
-pub const BOX: [&str; 3] = ["alloc", "boxed", "Box"];
-pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
-pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
-pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
-pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"];
-pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
-pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
-pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
-pub const COPY: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"];
-pub const COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy"];
-pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
-pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"];
-pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
-pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
-pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
-pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
-pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
-pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
-pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
-pub const DROP: [&str; 3] = ["core", "mem", "drop"];
-pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
-#[cfg(feature = "internal-lints")]
-pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
-pub const EXIT: [&str; 3] = ["std", "process", "exit"];
-pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
-pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
-pub const FILE: [&str; 3] = ["std", "fs", "File"];
-pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
-pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
-pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
-pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
-pub const FN: [&str; 3] = ["core", "ops", "Fn"];
-pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"];
-pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"];
-pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
-pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
-pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
-pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
-pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
-pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
-pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
-#[cfg(feature = "internal-lints")]
-pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
-#[cfg(feature = "internal-lints")]
-pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
-pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
-pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
-pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
-pub const INTO: [&str; 3] = ["core", "convert", "Into"];
-pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
-pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
-pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
-pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"];
-pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"];
-pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
-#[cfg(feature = "internal-lints")]
-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 LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
-#[cfg(feature = "internal-lints")]
-pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
-pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
-pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
-pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "ManuallyDrop"];
-pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"];
-pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"];
-pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
-pub const MEM_SIZE_OF: [&str; 3] = ["core", "mem", "size_of"];
-pub const MEM_SIZE_OF_VAL: [&str; 3] = ["core", "mem", "size_of_val"];
-pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
-pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
-pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
-pub const OPTION: [&str; 3] = ["core", "option", "Option"];
-pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
-pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
-pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
-pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"];
-pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
-pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
-pub(super) const PANICKING_PANIC: [&str; 3] = ["core", "panicking", "panic"];
-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_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 PATH: [&str; 3] = ["std", "path", "Path"];
-pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
-pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
-pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
-pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
-pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
-pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
-pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
-pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"];
-pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"];
-pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
-pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
-pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
-pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
-pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
-pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
-pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
-pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"];
-pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
-pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
-pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
-pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
-pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
-pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
-pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
-pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
-pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"];
-pub const RESULT: [&str; 3] = ["core", "result", "Result"];
-pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
-pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
-pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
-pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
-pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
-pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
-pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
-pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"];
-pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
-pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
-pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
-pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
-pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"];
-pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
-pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"];
-pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"];
-pub const STRING: [&str; 3] = ["alloc", "string", "String"];
-pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
-pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
-pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
-pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
-pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
-pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
-#[cfg(feature = "internal-lints")]
-pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
-#[cfg(feature = "internal-lints")]
-pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
-#[cfg(feature = "internal-lints")]
-pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
-#[cfg(feature = "internal-lints")]
-pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
-#[cfg(feature = "internal-lints")]
-pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
-#[cfg(feature = "internal-lints")]
-pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
-pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
-pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
-pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
-pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
-pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
-pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
-pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"];
-pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
-pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
-pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
-pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
-pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
-pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
-pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
-pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
-pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
-pub const WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
diff --git a/src/tools/clippy/clippy_lints/src/utils/ptr.rs b/src/tools/clippy/clippy_lints/src/utils/ptr.rs
deleted file mode 100644 (file)
index b330f3d..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-use crate::utils::{get_pat_name, match_var, snippet};
-use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{Body, BodyId, Expr, ExprKind, Param};
-use rustc_lint::LateContext;
-use rustc_middle::hir::map::Map;
-use rustc_span::{Span, Symbol};
-use std::borrow::Cow;
-
-pub fn get_spans(
-    cx: &LateContext<'_>,
-    opt_body_id: Option<BodyId>,
-    idx: usize,
-    replacements: &[(&'static str, &'static str)],
-) -> Option<Vec<(Span, Cow<'static, str>)>> {
-    if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) {
-        get_binding_name(&body.params[idx]).map_or_else(
-            || Some(vec![]),
-            |name| extract_clone_suggestions(cx, name, replacements, body),
-        )
-    } else {
-        Some(vec![])
-    }
-}
-
-fn extract_clone_suggestions<'tcx>(
-    cx: &LateContext<'tcx>,
-    name: Symbol,
-    replace: &[(&'static str, &'static str)],
-    body: &'tcx Body<'_>,
-) -> Option<Vec<(Span, Cow<'static, str>)>> {
-    let mut visitor = PtrCloneVisitor {
-        cx,
-        name,
-        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>,
-    name: Symbol,
-    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;
-        }
-        if let ExprKind::MethodCall(ref seg, _, ref args, _) = expr.kind {
-            if args.len() == 1 && match_var(&args[0], self.name) {
-                if seg.ident.name.as_str() == "capacity" {
-                    self.abort = true;
-                    return;
-                }
-                for &(fn_name, suffix) in self.replace {
-                    if seg.ident.name.as_str() == fn_name {
-                        self.spans
-                            .push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
-                        return;
-                    }
-                }
-            }
-        }
-        walk_expr(self, expr);
-    }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
-}
-
-fn get_binding_name(arg: &Param<'_>) -> Option<Symbol> {
-    get_pat_name(&arg.pat)
-}
diff --git a/src/tools/clippy/clippy_lints/src/utils/qualify_min_const_fn.rs b/src/tools/clippy/clippy_lints/src/utils/qualify_min_const_fn.rs
deleted file mode 100644 (file)
index a482017..0000000
+++ /dev/null
@@ -1,347 +0,0 @@
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_middle::mir::{
-    Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
-    TerminatorKind,
-};
-use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
-use rustc_span::symbol::sym;
-use rustc_span::Span;
-use rustc_target::spec::abi::Abi::RustIntrinsic;
-use std::borrow::Cow;
-
-type McfResult = Result<(), (Span, Cow<'static, str>)>;
-
-pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
-    let def_id = body.source.def_id();
-    let mut current = def_id;
-    loop {
-        let predicates = tcx.predicates_of(current);
-        for (predicate, _) in predicates.predicates {
-            match predicate.kind().skip_binder() {
-                ty::PredicateKind::RegionOutlives(_)
-                | ty::PredicateKind::TypeOutlives(_)
-                | ty::PredicateKind::WellFormed(_)
-                | ty::PredicateKind::Projection(_)
-                | ty::PredicateKind::ConstEvaluatable(..)
-                | ty::PredicateKind::ConstEquate(..)
-                | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
-                ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
-                ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
-                ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
-                ty::PredicateKind::Trait(pred, _) => {
-                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
-                        continue;
-                    }
-                    match pred.self_ty().kind() {
-                        ty::Param(ref p) => {
-                            let generics = tcx.generics_of(current);
-                            let def = generics.type_param(p, tcx);
-                            let span = tcx.def_span(def.def_id);
-                            return Err((
-                                span,
-                                "trait bounds other than `Sized` \
-                                 on const fn parameters are unstable"
-                                    .into(),
-                            ));
-                        },
-                        // other kinds of bounds are either tautologies
-                        // or cause errors in other passes
-                        _ => continue,
-                    }
-                },
-            }
-        }
-        match predicates.parent {
-            Some(parent) => current = parent,
-            None => break,
-        }
-    }
-
-    for local in &body.local_decls {
-        check_ty(tcx, local.ty, local.source_info.span)?;
-    }
-    // impl trait is gone in MIR, so check the return type manually
-    check_ty(
-        tcx,
-        tcx.fn_sig(def_id).output().skip_binder(),
-        body.local_decls.iter().next().unwrap().source_info.span,
-    )?;
-
-    for bb in body.basic_blocks() {
-        check_terminator(tcx, body, bb.terminator())?;
-        for stmt in &bb.statements {
-            check_statement(tcx, body, def_id, stmt)?;
-        }
-    }
-    Ok(())
-}
-
-fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
-    for arg in ty.walk() {
-        let ty = match arg.unpack() {
-            GenericArgKind::Type(ty) => ty,
-
-            // No constraints on lifetimes or constants, except potentially
-            // constants' types, but `walk` will get to them as well.
-            GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
-        };
-
-        match ty.kind() {
-            ty::Ref(_, _, hir::Mutability::Mut) => {
-                return Err((span, "mutable references in const fn are unstable".into()));
-            },
-            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
-            ty::FnPtr(..) => {
-                return Err((span, "function pointers in const fn are unstable".into()));
-            },
-            ty::Dynamic(preds, _) => {
-                for pred in preds.iter() {
-                    match pred.skip_binder() {
-                        ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
-                            return Err((
-                                span,
-                                "trait bounds other than `Sized` \
-                                 on const fn parameters are unstable"
-                                    .into(),
-                            ));
-                        },
-                        ty::ExistentialPredicate::Trait(trait_ref) => {
-                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
-                                return Err((
-                                    span,
-                                    "trait bounds other than `Sized` \
-                                     on const fn parameters are unstable"
-                                        .into(),
-                                ));
-                            }
-                        },
-                    }
-                }
-            },
-            _ => {},
-        }
-    }
-    Ok(())
-}
-
-fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
-    match rvalue {
-        Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
-        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
-        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
-            check_place(tcx, *place, span, body)
-        },
-        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
-            use rustc_middle::ty::cast::CastTy;
-            let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
-            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
-            match (cast_in, cast_out) {
-                (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
-                    Err((span, "casting pointers to ints is unstable in const fn".into()))
-                },
-                _ => check_operand(tcx, operand, span, body),
-            }
-        },
-        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
-            check_operand(tcx, operand, span, body)
-        },
-        Rvalue::Cast(
-            CastKind::Pointer(
-                PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
-            ),
-            _,
-            _,
-        ) => Err((span, "function pointer casts are not allowed in const fn".into())),
-        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
-            let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
-                deref_ty.ty
-            } else {
-                // We cannot allow this for now.
-                return Err((span, "unsizing casts are only allowed for references right now".into()));
-            };
-            let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
-            if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
-                check_operand(tcx, op, span, body)?;
-                // Casting/coercing things to slices is fine.
-                Ok(())
-            } else {
-                // We just can't allow trait objects until we have figured out trait method calls.
-                Err((span, "unsizing casts are not allowed in const fn".into()))
-            }
-        },
-        // binops are fine on integers
-        Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
-            check_operand(tcx, lhs, span, body)?;
-            check_operand(tcx, rhs, span, body)?;
-            let ty = lhs.ty(body, tcx);
-            if ty.is_integral() || ty.is_bool() || ty.is_char() {
-                Ok(())
-            } else {
-                Err((
-                    span,
-                    "only int, `bool` and `char` operations are stable in const fn".into(),
-                ))
-            }
-        },
-        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
-        Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
-        Rvalue::UnaryOp(_, operand) => {
-            let ty = operand.ty(body, tcx);
-            if ty.is_integral() || ty.is_bool() {
-                check_operand(tcx, operand, span, body)
-            } else {
-                Err((span, "only int and `bool` operations are stable in const fn".into()))
-            }
-        },
-        Rvalue::Aggregate(_, operands) => {
-            for operand in operands {
-                check_operand(tcx, operand, span, body)?;
-            }
-            Ok(())
-        },
-    }
-}
-
-fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
-    let span = statement.source_info.span;
-    match &statement.kind {
-        StatementKind::Assign(box (place, rval)) => {
-            check_place(tcx, *place, span, body)?;
-            check_rvalue(tcx, body, def_id, rval, span)
-        },
-
-        StatementKind::FakeRead(_, place) |
-        // just an assignment
-        StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),
-
-        StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
-
-        // These are all NOPs
-        StatementKind::StorageLive(_)
-        | StatementKind::StorageDead(_)
-        | StatementKind::Retag { .. }
-        | StatementKind::AscribeUserType(..)
-        | StatementKind::Coverage(..)
-        | StatementKind::Nop => Ok(()),
-    }
-}
-
-fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
-    match operand {
-        Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
-        Operand::Constant(c) => match c.check_static_ptr(tcx) {
-            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
-            None => Ok(()),
-        },
-    }
-}
-
-fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
-    let mut cursor = place.projection.as_ref();
-    while let [ref proj_base @ .., elem] = *cursor {
-        cursor = proj_base;
-        match elem {
-            ProjectionElem::Field(..) => {
-                let base_ty = Place::ty_from(place.local, &proj_base, body, tcx).ty;
-                if let Some(def) = base_ty.ty_adt_def() {
-                    // No union field accesses in `const fn`
-                    if def.is_union() {
-                        return Err((span, "accessing union fields is unstable".into()));
-                    }
-                }
-            },
-            ProjectionElem::ConstantIndex { .. }
-            | ProjectionElem::Downcast(..)
-            | ProjectionElem::Subslice { .. }
-            | ProjectionElem::Deref
-            | ProjectionElem::Index(_) => {},
-        }
-    }
-
-    Ok(())
-}
-
-fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
-    let span = terminator.source_info.span;
-    match &terminator.kind {
-        TerminatorKind::FalseEdge { .. }
-        | TerminatorKind::FalseUnwind { .. }
-        | TerminatorKind::Goto { .. }
-        | TerminatorKind::Return
-        | TerminatorKind::Resume
-        | TerminatorKind::Unreachable => Ok(()),
-
-        TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
-        TerminatorKind::DropAndReplace { place, value, .. } => {
-            check_place(tcx, *place, span, body)?;
-            check_operand(tcx, value, span, body)
-        },
-
-        TerminatorKind::SwitchInt {
-            discr,
-            switch_ty: _,
-            targets: _,
-        } => check_operand(tcx, discr, span, body),
-
-        TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
-        TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
-            Err((span, "const fn generators are unstable".into()))
-        },
-
-        TerminatorKind::Call {
-            func,
-            args,
-            from_hir_call: _,
-            destination: _,
-            cleanup: _,
-            fn_span: _,
-        } => {
-            let fn_ty = func.ty(body, tcx);
-            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
-                if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
-                    return Err((
-                        span,
-                        format!(
-                            "can only call other `const fn` within a `const fn`, \
-                             but `{:?}` is not stable as `const fn`",
-                            func,
-                        )
-                        .into(),
-                    ));
-                }
-
-                // HACK: This is to "unstabilize" the `transmute` intrinsic
-                // within const fns. `transmute` is allowed in all other const contexts.
-                // This won't really scale to more intrinsics or functions. Let's allow const
-                // transmutes in const fn before we add more hacks to this.
-                if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
-                    return Err((
-                        span,
-                        "can only call `transmute` from const items, not `const fn`".into(),
-                    ));
-                }
-
-                check_operand(tcx, func, span, body)?;
-
-                for arg in args {
-                    check_operand(tcx, arg, span, body)?;
-                }
-                Ok(())
-            } else {
-                Err((span, "can only call other const fns within const fn".into()))
-            }
-        },
-
-        TerminatorKind::Assert {
-            cond,
-            expected: _,
-            msg: _,
-            target: _,
-            cleanup: _,
-        } => check_operand(tcx, cond, span, body),
-
-        TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/utils/sugg.rs b/src/tools/clippy/clippy_lints/src/utils/sugg.rs
deleted file mode 100644 (file)
index 03678db..0000000
+++ /dev/null
@@ -1,683 +0,0 @@
-//! Contains utility functions to generate suggestions.
-#![deny(clippy::missing_docs_in_private_items)]
-
-use crate::utils::{higher, snippet, snippet_opt, snippet_with_macro_callsite};
-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_lint::{EarlyContext, LateContext, LintContext};
-use rustc_span::source_map::{CharPos, Span};
-use rustc_span::{BytePos, Pos};
-use std::borrow::Cow;
-use std::convert::TryInto;
-use std::fmt::Display;
-use std::ops::{Add, Neg, Not, Sub};
-
-/// A helper type to build suggestion correctly handling parenthesis.
-#[derive(Clone, PartialEq)]
-pub enum Sugg<'a> {
-    /// An expression that never needs parenthesis such as `1337` or `[0; 42]`.
-    NonParen(Cow<'a, str>),
-    /// An expression that does not fit in other variants.
-    MaybeParen(Cow<'a, str>),
-    /// A binary operator expression, including `as`-casts and explicit type
-    /// coercion.
-    BinOp(AssocOp, Cow<'a, str>),
-}
-
-/// Literal constant `0`, for convenience.
-pub const ZERO: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("0"));
-/// Literal constant `1`, for convenience.
-pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
-/// a constant represents an empty string, for convenience.
-pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed(""));
-
-impl Display for Sugg<'_> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
-        match *self {
-            Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => s.fmt(f),
-        }
-    }
-}
-
-#[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method
-impl<'a> Sugg<'a> {
-    /// Prepare a suggestion from an expression.
-    pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Self> {
-        snippet_opt(cx, expr.span).map(|snippet| {
-            let snippet = Cow::Owned(snippet);
-            Self::hir_from_snippet(expr, snippet)
-        })
-    }
-
-    /// Convenience function around `hir_opt` for suggestions with a default
-    /// text.
-    pub fn hir(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
-        Self::hir_opt(cx, expr).unwrap_or(Sugg::NonParen(Cow::Borrowed(default)))
-    }
-
-    /// Same as `hir`, but it adapts the applicability level by following rules:
-    ///
-    /// - Applicability level `Unspecified` will never be changed.
-    /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
-    /// - If the default value is used and the applicability level is `MachineApplicable`, change it
-    ///   to
-    /// `HasPlaceholders`
-    pub fn hir_with_applicability(
-        cx: &LateContext<'_>,
-        expr: &hir::Expr<'_>,
-        default: &'a str,
-        applicability: &mut Applicability,
-    ) -> Self {
-        if *applicability != Applicability::Unspecified && expr.span.from_expansion() {
-            *applicability = Applicability::MaybeIncorrect;
-        }
-        Self::hir_opt(cx, expr).unwrap_or_else(|| {
-            if *applicability == Applicability::MachineApplicable {
-                *applicability = Applicability::HasPlaceholders;
-            }
-            Sugg::NonParen(Cow::Borrowed(default))
-        })
-    }
-
-    /// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro.
-    pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
-        let snippet = snippet_with_macro_callsite(cx, expr.span, default);
-
-        Self::hir_from_snippet(expr, snippet)
-    }
-
-    /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
-    /// function variants of `Sugg`, since these use different snippet functions.
-    fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self {
-        if let Some(range) = higher::range(expr) {
-            let op = match range.limits {
-                ast::RangeLimits::HalfOpen => AssocOp::DotDot,
-                ast::RangeLimits::Closed => AssocOp::DotDotEq,
-            };
-            return Sugg::BinOp(op, snippet);
-        }
-
-        match expr.kind {
-            hir::ExprKind::AddrOf(..)
-            | hir::ExprKind::Box(..)
-            | hir::ExprKind::If(..)
-            | hir::ExprKind::Closure(..)
-            | hir::ExprKind::Unary(..)
-            | hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
-            hir::ExprKind::Continue(..)
-            | hir::ExprKind::Yield(..)
-            | hir::ExprKind::Array(..)
-            | hir::ExprKind::Block(..)
-            | hir::ExprKind::Break(..)
-            | hir::ExprKind::Call(..)
-            | hir::ExprKind::Field(..)
-            | hir::ExprKind::Index(..)
-            | hir::ExprKind::InlineAsm(..)
-            | hir::ExprKind::LlvmInlineAsm(..)
-            | hir::ExprKind::ConstBlock(..)
-            | hir::ExprKind::Lit(..)
-            | hir::ExprKind::Loop(..)
-            | hir::ExprKind::MethodCall(..)
-            | hir::ExprKind::Path(..)
-            | hir::ExprKind::Repeat(..)
-            | hir::ExprKind::Ret(..)
-            | hir::ExprKind::Struct(..)
-            | hir::ExprKind::Tup(..)
-            | hir::ExprKind::DropTemps(_)
-            | hir::ExprKind::Err => Sugg::NonParen(snippet),
-            hir::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
-            hir::ExprKind::AssignOp(op, ..) => Sugg::BinOp(hirbinop2assignop(op), snippet),
-            hir::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(higher::binop(op.node)), snippet),
-            hir::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
-            hir::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
-        }
-    }
-
-    /// Prepare a suggestion from an expression.
-    pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
-        use rustc_ast::ast::RangeLimits;
-
-        let snippet = if expr.span.from_expansion() {
-            snippet_with_macro_callsite(cx, expr.span, default)
-        } else {
-            snippet(cx, expr.span, default)
-        };
-
-        match expr.kind {
-            ast::ExprKind::AddrOf(..)
-            | ast::ExprKind::Box(..)
-            | ast::ExprKind::Closure(..)
-            | ast::ExprKind::If(..)
-            | ast::ExprKind::Let(..)
-            | ast::ExprKind::Unary(..)
-            | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
-            ast::ExprKind::Async(..)
-            | ast::ExprKind::Block(..)
-            | ast::ExprKind::Break(..)
-            | ast::ExprKind::Call(..)
-            | ast::ExprKind::Continue(..)
-            | ast::ExprKind::Yield(..)
-            | ast::ExprKind::Field(..)
-            | ast::ExprKind::ForLoop(..)
-            | ast::ExprKind::Index(..)
-            | ast::ExprKind::InlineAsm(..)
-            | ast::ExprKind::LlvmInlineAsm(..)
-            | ast::ExprKind::ConstBlock(..)
-            | ast::ExprKind::Lit(..)
-            | ast::ExprKind::Loop(..)
-            | ast::ExprKind::MacCall(..)
-            | ast::ExprKind::MethodCall(..)
-            | ast::ExprKind::Paren(..)
-            | ast::ExprKind::Underscore
-            | ast::ExprKind::Path(..)
-            | ast::ExprKind::Repeat(..)
-            | ast::ExprKind::Ret(..)
-            | ast::ExprKind::Struct(..)
-            | ast::ExprKind::Try(..)
-            | ast::ExprKind::TryBlock(..)
-            | ast::ExprKind::Tup(..)
-            | ast::ExprKind::Array(..)
-            | ast::ExprKind::While(..)
-            | ast::ExprKind::Await(..)
-            | ast::ExprKind::Err => Sugg::NonParen(snippet),
-            ast::ExprKind::Range(.., RangeLimits::HalfOpen) => Sugg::BinOp(AssocOp::DotDot, snippet),
-            ast::ExprKind::Range(.., RangeLimits::Closed) => Sugg::BinOp(AssocOp::DotDotEq, snippet),
-            ast::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
-            ast::ExprKind::AssignOp(op, ..) => Sugg::BinOp(astbinop2assignop(op), snippet),
-            ast::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node), snippet),
-            ast::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
-            ast::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
-        }
-    }
-
-    /// Convenience method to create the `<lhs> && <rhs>` suggestion.
-    pub fn and(self, rhs: &Self) -> Sugg<'static> {
-        make_binop(ast::BinOpKind::And, &self, rhs)
-    }
-
-    /// Convenience method to create the `<lhs> & <rhs>` suggestion.
-    pub fn bit_and(self, rhs: &Self) -> Sugg<'static> {
-        make_binop(ast::BinOpKind::BitAnd, &self, rhs)
-    }
-
-    /// Convenience method to create the `<lhs> as <rhs>` suggestion.
-    pub fn as_ty<R: Display>(self, rhs: R) -> Sugg<'static> {
-        make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into()))
-    }
-
-    /// Convenience method to create the `&<expr>` suggestion.
-    pub fn addr(self) -> Sugg<'static> {
-        make_unop("&", self)
-    }
-
-    /// Convenience method to create the `&mut <expr>` suggestion.
-    pub fn mut_addr(self) -> Sugg<'static> {
-        make_unop("&mut ", self)
-    }
-
-    /// Convenience method to create the `*<expr>` suggestion.
-    pub fn deref(self) -> Sugg<'static> {
-        make_unop("*", self)
-    }
-
-    /// Convenience method to create the `&*<expr>` suggestion. Currently this
-    /// is needed because `sugg.deref().addr()` produces an unnecessary set of
-    /// parentheses around the deref.
-    pub fn addr_deref(self) -> Sugg<'static> {
-        make_unop("&*", self)
-    }
-
-    /// Convenience method to create the `&mut *<expr>` suggestion. Currently
-    /// this is needed because `sugg.deref().mut_addr()` produces an unnecessary
-    /// set of parentheses around the deref.
-    pub fn mut_addr_deref(self) -> Sugg<'static> {
-        make_unop("&mut *", self)
-    }
-
-    /// Convenience method to transform suggestion into a return call
-    pub fn make_return(self) -> Sugg<'static> {
-        Sugg::NonParen(Cow::Owned(format!("return {}", self)))
-    }
-
-    /// Convenience method to transform suggestion into a block
-    /// where the suggestion is a trailing expression
-    pub fn blockify(self) -> Sugg<'static> {
-        Sugg::NonParen(Cow::Owned(format!("{{ {} }}", self)))
-    }
-
-    /// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
-    /// suggestion.
-    #[allow(dead_code)]
-    pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> {
-        match limit {
-            ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end),
-            ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, end),
-        }
-    }
-
-    /// Adds parenthesis to any expression that might need them. Suitable to the
-    /// `self` argument of a method call
-    /// (e.g., to build `bar.foo()` or `(1 + 2).foo()`).
-    pub fn maybe_par(self) -> Self {
-        match self {
-            Sugg::NonParen(..) => self,
-            // `(x)` and `(x).y()` both don't need additional parens.
-            Sugg::MaybeParen(sugg) => {
-                if sugg.starts_with('(') && sugg.ends_with(')') {
-                    Sugg::MaybeParen(sugg)
-                } else {
-                    Sugg::NonParen(format!("({})", sugg).into())
-                }
-            },
-            Sugg::BinOp(_, sugg) => Sugg::NonParen(format!("({})", sugg).into()),
-        }
-    }
-}
-
-// Copied from the rust standart library, and then edited
-macro_rules! forward_binop_impls_to_ref {
-    (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => {
-        impl $imp<$t> for &$t {
-            type Output = $o;
-
-            fn $method(self, other: $t) -> $o {
-                $imp::$method(self, &other)
-            }
-        }
-
-        impl $imp<&$t> for $t {
-            type Output = $o;
-
-            fn $method(self, other: &$t) -> $o {
-                $imp::$method(&self, other)
-            }
-        }
-
-        impl $imp for $t {
-            type Output = $o;
-
-            fn $method(self, other: $t) -> $o {
-                $imp::$method(&self, &other)
-            }
-        }
-    };
-}
-
-impl Add for &Sugg<'_> {
-    type Output = Sugg<'static>;
-    fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> {
-        make_binop(ast::BinOpKind::Add, self, rhs)
-    }
-}
-
-impl Sub for &Sugg<'_> {
-    type Output = Sugg<'static>;
-    fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> {
-        make_binop(ast::BinOpKind::Sub, self, rhs)
-    }
-}
-
-forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>);
-forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>);
-
-impl Neg for Sugg<'_> {
-    type Output = Sugg<'static>;
-    fn neg(self) -> Sugg<'static> {
-        make_unop("-", self)
-    }
-}
-
-impl Not for Sugg<'_> {
-    type Output = Sugg<'static>;
-    fn not(self) -> Sugg<'static> {
-        make_unop("!", self)
-    }
-}
-
-/// Helper type to display either `foo` or `(foo)`.
-struct ParenHelper<T> {
-    /// `true` if parentheses are needed.
-    paren: bool,
-    /// The main thing to display.
-    wrapped: T,
-}
-
-impl<T> ParenHelper<T> {
-    /// Builds a `ParenHelper`.
-    fn new(paren: bool, wrapped: T) -> Self {
-        Self { paren, wrapped }
-    }
-}
-
-impl<T: Display> Display for ParenHelper<T> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
-        if self.paren {
-            write!(f, "({})", self.wrapped)
-        } else {
-            self.wrapped.fmt(f)
-        }
-    }
-}
-
-/// Builds the string for `<op><expr>` adding parenthesis when necessary.
-///
-/// For convenience, the operator is taken as a string because all unary
-/// operators have the same
-/// precedence.
-pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
-    Sugg::MaybeParen(format!("{}{}", op, expr.maybe_par()).into())
-}
-
-/// Builds the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
-///
-/// Precedence of shift operator relative to other arithmetic operation is
-/// often confusing so
-/// parenthesis will always be added for a mix of these.
-pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
-    /// Returns `true` if the operator is a shift operator `<<` or `>>`.
-    fn is_shift(op: AssocOp) -> bool {
-        matches!(op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
-    }
-
-    /// Returns `true` if the operator is a arithmetic operator
-    /// (i.e., `+`, `-`, `*`, `/`, `%`).
-    fn is_arith(op: AssocOp) -> bool {
-        matches!(
-            op,
-            AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus
-        )
-    }
-
-    /// Returns `true` if the operator `op` needs parenthesis with the operator
-    /// `other` in the direction `dir`.
-    fn needs_paren(op: AssocOp, other: AssocOp, dir: Associativity) -> bool {
-        other.precedence() < op.precedence()
-            || (other.precedence() == op.precedence()
-                && ((op != other && associativity(op) != dir)
-                    || (op == other && associativity(op) != Associativity::Both)))
-            || is_shift(op) && is_arith(other)
-            || is_shift(other) && is_arith(op)
-    }
-
-    let lhs_paren = if let Sugg::BinOp(lop, _) = *lhs {
-        needs_paren(op, lop, Associativity::Left)
-    } else {
-        false
-    };
-
-    let rhs_paren = if let Sugg::BinOp(rop, _) = *rhs {
-        needs_paren(op, rop, Associativity::Right)
-    } else {
-        false
-    };
-
-    let lhs = ParenHelper::new(lhs_paren, lhs);
-    let rhs = ParenHelper::new(rhs_paren, rhs);
-    let sugg = match op {
-        AssocOp::Add
-        | AssocOp::BitAnd
-        | AssocOp::BitOr
-        | AssocOp::BitXor
-        | AssocOp::Divide
-        | AssocOp::Equal
-        | AssocOp::Greater
-        | AssocOp::GreaterEqual
-        | AssocOp::LAnd
-        | AssocOp::LOr
-        | AssocOp::Less
-        | AssocOp::LessEqual
-        | AssocOp::Modulus
-        | AssocOp::Multiply
-        | AssocOp::NotEqual
-        | AssocOp::ShiftLeft
-        | AssocOp::ShiftRight
-        | AssocOp::Subtract => format!(
-            "{} {} {}",
-            lhs,
-            op.to_ast_binop().expect("Those are AST ops").to_string(),
-            rhs
-        ),
-        AssocOp::Assign => format!("{} = {}", lhs, rhs),
-        AssocOp::AssignOp(op) => format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs),
-        AssocOp::As => format!("{} as {}", lhs, rhs),
-        AssocOp::DotDot => format!("{}..{}", lhs, rhs),
-        AssocOp::DotDotEq => format!("{}..={}", lhs, rhs),
-        AssocOp::Colon => format!("{}: {}", lhs, rhs),
-    };
-
-    Sugg::BinOp(op, sugg.into())
-}
-
-/// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`.
-pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
-    make_assoc(AssocOp::from_ast_binop(op), lhs, rhs)
-}
-
-#[derive(PartialEq, Eq, Clone, Copy)]
-/// Operator associativity.
-enum Associativity {
-    /// The operator is both left-associative and right-associative.
-    Both,
-    /// The operator is left-associative.
-    Left,
-    /// The operator is not associative.
-    None,
-    /// The operator is right-associative.
-    Right,
-}
-
-/// Returns the associativity/fixity of an operator. The difference with
-/// `AssocOp::fixity` is that an operator can be both left and right associative
-/// (such as `+`: `a + b + c == (a + b) + c == a + (b + c)`.
-///
-/// Chained `as` and explicit `:` type coercion never need inner parenthesis so
-/// they are considered
-/// associative.
-#[must_use]
-fn associativity(op: AssocOp) -> Associativity {
-    use rustc_ast::util::parser::AssocOp::{
-        Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Colon, Divide, DotDot, DotDotEq, Equal, Greater,
-        GreaterEqual, LAnd, LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract,
-    };
-
-    match op {
-        Assign | AssignOp(_) => Associativity::Right,
-        Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both,
-        Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight
-        | Subtract => Associativity::Left,
-        DotDot | DotDotEq => Associativity::None,
-    }
-}
-
-/// Converts a `hir::BinOp` to the corresponding assigning binary operator.
-fn hirbinop2assignop(op: hir::BinOp) -> AssocOp {
-    use rustc_ast::token::BinOpToken::{And, Caret, Minus, Or, Percent, Plus, Shl, Shr, Slash, Star};
-
-    AssocOp::AssignOp(match op.node {
-        hir::BinOpKind::Add => Plus,
-        hir::BinOpKind::BitAnd => And,
-        hir::BinOpKind::BitOr => Or,
-        hir::BinOpKind::BitXor => Caret,
-        hir::BinOpKind::Div => Slash,
-        hir::BinOpKind::Mul => Star,
-        hir::BinOpKind::Rem => Percent,
-        hir::BinOpKind::Shl => Shl,
-        hir::BinOpKind::Shr => Shr,
-        hir::BinOpKind::Sub => Minus,
-
-        hir::BinOpKind::And
-        | hir::BinOpKind::Eq
-        | hir::BinOpKind::Ge
-        | hir::BinOpKind::Gt
-        | hir::BinOpKind::Le
-        | hir::BinOpKind::Lt
-        | hir::BinOpKind::Ne
-        | hir::BinOpKind::Or => panic!("This operator does not exist"),
-    })
-}
-
-/// Converts an `ast::BinOp` to the corresponding assigning binary operator.
-fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
-    use rustc_ast::ast::BinOpKind::{
-        Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
-    };
-    use rustc_ast::token::BinOpToken;
-
-    AssocOp::AssignOp(match op.node {
-        Add => BinOpToken::Plus,
-        BitAnd => BinOpToken::And,
-        BitOr => BinOpToken::Or,
-        BitXor => BinOpToken::Caret,
-        Div => BinOpToken::Slash,
-        Mul => BinOpToken::Star,
-        Rem => BinOpToken::Percent,
-        Shl => BinOpToken::Shl,
-        Shr => BinOpToken::Shr,
-        Sub => BinOpToken::Minus,
-        And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"),
-    })
-}
-
-/// Returns the indentation before `span` if there are nothing but `[ \t]`
-/// before it on its line.
-fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
-    let lo = cx.sess().source_map().lookup_char_pos(span.lo());
-    lo.file
-        .get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */)
-        .and_then(|line| {
-            if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
-                // We can mix char and byte positions here because we only consider `[ \t]`.
-                if lo.col == CharPos(pos) {
-                    Some(line[..pos].into())
-                } else {
-                    None
-                }
-            } else {
-                None
-            }
-        })
-}
-
-/// Convenience extension trait for `DiagnosticBuilder`.
-pub trait DiagnosticBuilderExt<T: LintContext> {
-    /// Suggests to add an attribute to an item.
-    ///
-    /// Correctly handles indentation of the attribute and item.
-    ///
-    /// # Example
-    ///
-    /// ```rust,ignore
-    /// diag.suggest_item_with_attr(cx, item, "#[derive(Default)]");
-    /// ```
-    fn suggest_item_with_attr<D: Display + ?Sized>(
-        &mut self,
-        cx: &T,
-        item: Span,
-        msg: &str,
-        attr: &D,
-        applicability: Applicability,
-    );
-
-    /// Suggest to add an item before another.
-    ///
-    /// The item should not be indented (except for inner indentation).
-    ///
-    /// # Example
-    ///
-    /// ```rust,ignore
-    /// diag.suggest_prepend_item(cx, item,
-    /// "fn foo() {
-    ///     bar();
-    /// }");
-    /// ```
-    fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability);
-
-    /// Suggest to completely remove an item.
-    ///
-    /// This will remove an item and all following whitespace until the next non-whitespace
-    /// character. This should work correctly if item is on the same indentation level as the
-    /// following item.
-    ///
-    /// # Example
-    ///
-    /// ```rust,ignore
-    /// diag.suggest_remove_item(cx, item, "remove this")
-    /// ```
-    fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability);
-}
-
-impl<T: LintContext> DiagnosticBuilderExt<T> for rustc_errors::DiagnosticBuilder<'_> {
-    fn suggest_item_with_attr<D: Display + ?Sized>(
-        &mut self,
-        cx: &T,
-        item: Span,
-        msg: &str,
-        attr: &D,
-        applicability: Applicability,
-    ) {
-        if let Some(indent) = indentation(cx, item) {
-            let span = item.with_hi(item.lo());
-
-            self.span_suggestion(span, msg, format!("{}\n{}", attr, indent), applicability);
-        }
-    }
-
-    fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability) {
-        if let Some(indent) = indentation(cx, item) {
-            let span = item.with_hi(item.lo());
-
-            let mut first = true;
-            let new_item = new_item
-                .lines()
-                .map(|l| {
-                    if first {
-                        first = false;
-                        format!("{}\n", l)
-                    } else {
-                        format!("{}{}\n", indent, l)
-                    }
-                })
-                .collect::<String>();
-
-            self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent), applicability);
-        }
-    }
-
-    fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) {
-        let mut remove_span = item;
-        let hi = cx.sess().source_map().next_point(remove_span).hi();
-        let fmpos = cx.sess().source_map().lookup_byte_offset(hi);
-
-        if let Some(ref src) = fmpos.sf.src {
-            let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n');
-
-            if let Some(non_whitespace_offset) = non_whitespace_offset {
-                remove_span = remove_span
-                    .with_hi(remove_span.hi() + BytePos(non_whitespace_offset.try_into().expect("offset too large")))
-            }
-        }
-
-        self.span_suggestion(remove_span, msg, String::new(), applicability);
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::Sugg;
-    use std::borrow::Cow;
-
-    const SUGGESTION: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("function_call()"));
-
-    #[test]
-    fn make_return_transform_sugg_into_a_return_call() {
-        assert_eq!("return function_call()", SUGGESTION.make_return().to_string());
-    }
-
-    #[test]
-    fn blockify_transforms_sugg_into_a_block() {
-        assert_eq!("{ function_call() }", SUGGESTION.blockify().to_string());
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/utils/sym_helper.rs b/src/tools/clippy/clippy_lints/src/utils/sym_helper.rs
deleted file mode 100644 (file)
index f47dc80..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#[macro_export]
-/// Convenience wrapper around rustc's `Symbol::intern`
-macro_rules! sym {
-    ($tt:tt) => {
-        rustc_span::symbol::Symbol::intern(stringify!($tt))
-    };
-}
diff --git a/src/tools/clippy/clippy_lints/src/utils/usage.rs b/src/tools/clippy/clippy_lints/src/utils/usage.rs
deleted file mode 100644 (file)
index 7c7580a..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-use crate::utils;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_hir as hir;
-use rustc_hir::def::Res;
-use rustc_hir::intravisit;
-use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
-use rustc_hir::{Expr, ExprKind, HirId, Path};
-use rustc_infer::infer::TyCtxtInferExt;
-use rustc_lint::LateContext;
-use rustc_middle::hir::map::Map;
-use rustc_middle::ty;
-use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
-
-/// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined.
-pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option<FxHashSet<HirId>> {
-    let mut delegate = MutVarsDelegate {
-        used_mutably: FxHashSet::default(),
-        skip: false,
-    };
-    cx.tcx.infer_ctxt().enter(|infcx| {
-        ExprUseVisitor::new(
-            &mut delegate,
-            &infcx,
-            expr.hir_id.owner,
-            cx.param_env,
-            cx.typeck_results(),
-        )
-        .walk_expr(expr);
-    });
-
-    if delegate.skip {
-        return None;
-    }
-    Some(delegate.used_mutably)
-}
-
-pub fn is_potentially_mutated<'tcx>(variable: &'tcx Path<'_>, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
-    if let Res::Local(id) = variable.res {
-        mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&id))
-    } else {
-        true
-    }
-}
-
-struct MutVarsDelegate {
-    used_mutably: FxHashSet<HirId>,
-    skip: bool,
-}
-
-impl<'tcx> MutVarsDelegate {
-    #[allow(clippy::similar_names)]
-    fn update(&mut self, cat: &PlaceWithHirId<'tcx>) {
-        match cat.place.base {
-            PlaceBase::Local(id) => {
-                self.used_mutably.insert(id);
-            },
-            PlaceBase::Upvar(_) => {
-                //FIXME: This causes false negatives. We can't get the `NodeId` from
-                //`Categorization::Upvar(_)`. So we search for any `Upvar`s in the
-                //`while`-body, not just the ones in the condition.
-                self.skip = true
-            },
-            _ => {},
-        }
-    }
-}
-
-impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
-    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ConsumeMode) {}
-
-    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, bk: ty::BorrowKind) {
-        if let ty::BorrowKind::MutBorrow = bk {
-            self.update(&cmt)
-        }
-    }
-
-    fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
-        self.update(&cmt)
-    }
-}
-
-pub struct ParamBindingIdCollector {
-    binding_hir_ids: Vec<hir::HirId>,
-}
-impl<'tcx> ParamBindingIdCollector {
-    fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
-        let mut hir_ids: Vec<hir::HirId> = Vec::new();
-        for param in body.params.iter() {
-            let mut finder = ParamBindingIdCollector {
-                binding_hir_ids: Vec::new(),
-            };
-            finder.visit_param(param);
-            for hir_id in &finder.binding_hir_ids {
-                hir_ids.push(*hir_id);
-            }
-        }
-        hir_ids
-    }
-}
-impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector {
-    type Map = Map<'tcx>;
-
-    fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
-        if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind {
-            self.binding_hir_ids.push(hir_id);
-        }
-        intravisit::walk_pat(self, pat);
-    }
-
-    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
-        intravisit::NestedVisitorMap::None
-    }
-}
-
-pub struct BindingUsageFinder<'a, 'tcx> {
-    cx: &'a LateContext<'tcx>,
-    binding_ids: Vec<hir::HirId>,
-    usage_found: bool,
-}
-impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> {
-    pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool {
-        let mut finder = BindingUsageFinder {
-            cx,
-            binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body),
-            usage_found: false,
-        };
-        finder.visit_body(body);
-        finder.usage_found
-    }
-}
-impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
-    type Map = Map<'tcx>;
-
-    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
-        if !self.usage_found {
-            intravisit::walk_expr(self, expr);
-        }
-    }
-
-    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
-        if let hir::def::Res::Local(id) = path.res {
-            if self.binding_ids.contains(&id) {
-                self.usage_found = true;
-            }
-        }
-    }
-
-    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
-        intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
-    }
-}
-
-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;
-        }
-        match &ex.kind {
-            ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
-                self.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);
-                }
-            },
-        }
-    }
-}
-
-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
-}
diff --git a/src/tools/clippy/clippy_lints/src/utils/visitors.rs b/src/tools/clippy/clippy_lints/src/utils/visitors.rs
deleted file mode 100644 (file)
index 085c1f9..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-use crate::utils::path_to_local_id;
-use rustc_hir as hir;
-use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{Arm, Body, Expr, HirId, Stmt};
-use rustc_lint::LateContext;
-use rustc_middle::hir::map::Map;
-
-/// returns `true` if expr contains match expr desugared from try
-fn contains_try(expr: &hir::Expr<'_>) -> bool {
-    struct TryFinder {
-        found: bool,
-    }
-
-    impl<'hir> intravisit::Visitor<'hir> for TryFinder {
-        type Map = Map<'hir>;
-
-        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
-            intravisit::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),
-            }
-        }
-    }
-
-    let mut visitor = TryFinder { found: false };
-    visitor.visit_expr(expr);
-    visitor.found
-}
-
-pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
-where
-    F: FnMut(&'hir hir::Expr<'hir>) -> bool,
-{
-    struct RetFinder<F> {
-        in_stmt: bool,
-        failed: bool,
-        cb: F,
-    }
-
-    struct WithStmtGuarg<'a, F> {
-        val: &'a mut RetFinder<F>,
-        prev_in_stmt: bool,
-    }
-
-    impl<F> RetFinder<F> {
-        fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
-            let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
-            WithStmtGuarg {
-                val: self,
-                prev_in_stmt,
-            }
-        }
-    }
-
-    impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
-        type Target = RetFinder<F>;
-
-        fn deref(&self) -> &Self::Target {
-            self.val
-        }
-    }
-
-    impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
-        fn deref_mut(&mut self) -> &mut Self::Target {
-            self.val
-        }
-    }
-
-    impl<F> Drop for WithStmtGuarg<'_, F> {
-        fn drop(&mut self) {
-            self.val.in_stmt = self.prev_in_stmt;
-        }
-    }
-
-    impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
-        type Map = Map<'hir>;
-
-        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
-            intravisit::NestedVisitorMap::None
-        }
-
-        fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) {
-            intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt)
-        }
-
-        fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
-            if self.failed {
-                return;
-            }
-            if self.in_stmt {
-                match expr.kind {
-                    hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
-                    _ => intravisit::walk_expr(self, expr),
-                }
-            } else {
-                match expr.kind {
-                    hir::ExprKind::If(cond, then, else_opt) => {
-                        self.inside_stmt(true).visit_expr(cond);
-                        self.visit_expr(then);
-                        if let Some(el) = else_opt {
-                            self.visit_expr(el);
-                        }
-                    },
-                    hir::ExprKind::Match(cond, arms, _) => {
-                        self.inside_stmt(true).visit_expr(cond);
-                        for arm in arms {
-                            self.visit_expr(arm.body);
-                        }
-                    },
-                    hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr),
-                    hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
-                    _ => self.failed |= !(self.cb)(expr),
-                }
-            }
-        }
-    }
-
-    !contains_try(expr) && {
-        let mut ret_finder = RetFinder {
-            in_stmt: false,
-            failed: false,
-            cb: callback,
-        };
-        ret_finder.visit_expr(expr);
-        !ret_finder.failed
-    }
-}
-
-pub struct LocalUsedVisitor<'hir> {
-    hir: Map<'hir>,
-    pub local_hir_id: HirId,
-    pub used: bool,
-}
-
-impl<'hir> LocalUsedVisitor<'hir> {
-    pub fn new(cx: &LateContext<'hir>, local_hir_id: HirId) -> Self {
-        Self {
-            hir: cx.tcx.hir(),
-            local_hir_id,
-            used: false,
-        }
-    }
-
-    fn check<T>(&mut self, t: T, visit: fn(&mut Self, T)) -> bool {
-        visit(self, t);
-        std::mem::replace(&mut self.used, false)
-    }
-
-    pub fn check_arm(&mut self, arm: &'hir Arm<'_>) -> bool {
-        self.check(arm, Self::visit_arm)
-    }
-
-    pub fn check_body(&mut self, body: &'hir Body<'_>) -> bool {
-        self.check(body, Self::visit_body)
-    }
-
-    pub fn check_expr(&mut self, expr: &'hir Expr<'_>) -> bool {
-        self.check(expr, Self::visit_expr)
-    }
-
-    pub fn check_stmt(&mut self, stmt: &'hir Stmt<'_>) -> bool {
-        self.check(stmt, Self::visit_stmt)
-    }
-}
-
-impl<'v> Visitor<'v> for LocalUsedVisitor<'v> {
-    type Map = Map<'v>;
-
-    fn visit_expr(&mut self, expr: &'v Expr<'v>) {
-        if self.used {
-            return;
-        }
-        if path_to_local_id(expr, self.local_hir_id) {
-            self.used = true;
-        } else {
-            walk_expr(self, expr);
-        }
-    }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::OnlyBodies(self.hir)
-    }
-}
index e632a7e57ee87b22fd351a80a3497bec5804a4de..8d111f98add9aac2c7ba145d443bbbf67cb5a594 100644 (file)
@@ -1,12 +1,14 @@
-use crate::utils::{is_type_diagnostic_item, match_def_path, paths, snippet, span_lint_and_sugg};
+use crate::utils::{
+    is_type_diagnostic_item, match_def_path, path_to_local, path_to_local_id, paths, snippet, span_lint_and_sugg,
+};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Local, PatKind, QPath, Stmt, StmtKind};
+use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, QPath, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{symbol::sym, Span, Symbol};
+use rustc_span::{symbol::sym, Span};
 use std::convert::TryInto;
 
 declare_clippy_lint! {
@@ -45,8 +47,8 @@ enum VecInitKind {
     WithCapacity(u64),
 }
 struct VecPushSearcher {
+    local_id: HirId,
     init: VecInitKind,
-    name: Symbol,
     lhs_is_local: bool,
     lhs_span: Span,
     err_span: Span,
@@ -81,17 +83,20 @@ fn display_err(&self, cx: &LateContext<'_>) {
 }
 
 impl LateLintPass<'_> for VecInitThenPush {
-    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+    fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) {
         self.searcher = None;
+    }
+
+    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
         if_chain! {
             if !in_external_macro(cx.sess(), local.span);
             if let Some(init) = local.init;
-            if let PatKind::Binding(BindingAnnotation::Mutable, _, ident, None) = local.pat.kind;
+            if let PatKind::Binding(BindingAnnotation::Mutable, id, _, None) = local.pat.kind;
             if let Some(init_kind) = get_vec_init_kind(cx, init);
             then {
                 self.searcher = Some(VecPushSearcher {
+                        local_id: id,
                         init: init_kind,
-                        name: ident.name,
                         lhs_is_local: true,
                         lhs_span: local.ty.map_or(local.pat.span, |t| local.pat.span.to(t.span)),
                         err_span: local.span,
@@ -106,13 +111,12 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             if_chain! {
                 if !in_external_macro(cx.sess(), expr.span);
                 if let ExprKind::Assign(left, right, _) = expr.kind;
-                if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind;
-                if let Some(name) = path.segments.get(0);
+                if let Some(id) = path_to_local(left);
                 if let Some(init_kind) = get_vec_init_kind(cx, right);
                 then {
                     self.searcher = Some(VecPushSearcher {
+                        local_id: id,
                         init: init_kind,
-                        name: name.ident.name,
                         lhs_is_local: false,
                         lhs_span: left.span,
                         err_span: expr.span,
@@ -128,10 +132,8 @@ fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
             if_chain! {
                 if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind;
                 if let ExprKind::MethodCall(path, _, [self_arg, _], _) = expr.kind;
+                if path_to_local_id(self_arg, searcher.local_id);
                 if path.ident.name.as_str() == "push";
-                if let ExprKind::Path(QPath::Resolved(_, self_path)) = self_arg.kind;
-                if let [self_name] = self_path.segments;
-                if self_name.ident.name == searcher.name;
                 then {
                     self.searcher = Some(VecPushSearcher {
                         found: searcher.found + 1,
index 978a232bcfb3a132a37ef2d6d09f6b5ede42c02a..553e6b000ebbc055161347f939a3060f206893b4 100644 (file)
     /// ```rust
     /// # use std::fmt::Write;
     /// # let mut buf = String::new();
-    ///
     /// // Bad
     /// writeln!(buf, "");
     ///
     /// # use std::fmt::Write;
     /// # let mut buf = String::new();
     /// # let name = "World";
-    ///
     /// // Bad
     /// write!(buf, "Hello {}!\n", name);
     ///
     /// ```rust
     /// # use std::fmt::Write;
     /// # let mut buf = String::new();
-    ///
     /// // Bad
     /// writeln!(buf, "{}", "foo");
     ///
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
new file mode 100644 (file)
index 0000000..9c01bad
--- /dev/null
@@ -0,0 +1,19 @@
+[package]
+name = "clippy_utils"
+version = "0.1.52"
+authors = ["The Rust Clippy Developers"]
+edition = "2018"
+publish = false
+
+[dependencies]
+if_chain = "1.0.0"
+itertools = "0.9"
+regex-syntax = "0.6"
+serde = { version = "1.0", features = ["derive"] }
+smallvec = { version = "1", features = ["union"] }
+toml = "0.5.3"
+unicode-normalization = "0.1"
+rustc-semver="1.1.0"
+
+[features]
+internal-lints = []
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
new file mode 100644 (file)
index 0000000..9ef1557
--- /dev/null
@@ -0,0 +1,571 @@
+//! Utilities for manipulating and extracting information from `rustc_ast::ast`.
+//!
+//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s.
+
+#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
+
+use crate::{both, over};
+use rustc_ast::ptr::P;
+use rustc_ast::{self as ast, *};
+use rustc_span::symbol::Ident;
+use std::mem;
+
+pub mod ident_iter;
+pub use ident_iter::IdentIter;
+
+pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool {
+    use BinOpKind::*;
+    matches!(
+        kind,
+        Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr
+    )
+}
+
+/// Checks if each element in the first slice is contained within the latter as per `eq_fn`.
+pub fn unordered_over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
+    left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r)))
+}
+
+pub fn eq_id(l: Ident, r: Ident) -> bool {
+    l.name == r.name
+}
+
+pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
+    use PatKind::*;
+    match (&l.kind, &r.kind) {
+        (Paren(l), _) => eq_pat(l, r),
+        (_, Paren(r)) => eq_pat(l, r),
+        (Wild, Wild) | (Rest, Rest) => true,
+        (Lit(l), Lit(r)) => eq_expr(l, r),
+        (Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)),
+        (Range(lf, lt, le), Range(rf, rt, re)) => {
+            eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node)
+        },
+        (Box(l), Box(r))
+        | (Ref(l, Mutability::Not), Ref(r, Mutability::Not))
+        | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
+        (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
+        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
+        (TupleStruct(lp, lfs), TupleStruct(rp, rfs)) => eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)),
+        (Struct(lp, lfs, lr), Struct(rp, rfs, rr)) => {
+            lr == rr && eq_path(lp, rp) && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
+        },
+        (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
+        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
+        _ => false,
+    }
+}
+
+pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool {
+    match (l, r) {
+        (RangeEnd::Excluded, RangeEnd::Excluded) => true,
+        (RangeEnd::Included(l), RangeEnd::Included(r)) => {
+            matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq)
+        },
+        _ => false,
+    }
+}
+
+pub fn eq_field_pat(l: &FieldPat, r: &FieldPat) -> bool {
+    l.is_placeholder == r.is_placeholder
+        && eq_id(l.ident, r.ident)
+        && eq_pat(&l.pat, &r.pat)
+        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+}
+
+pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool {
+    l.position == r.position && eq_ty(&l.ty, &r.ty)
+}
+
+pub fn eq_path(l: &Path, r: &Path) -> bool {
+    over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r))
+}
+
+pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool {
+    eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r))
+}
+
+pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool {
+    match (l, r) {
+        (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => {
+            over(&l.args, &r.args, |l, r| eq_angle_arg(l, r))
+        },
+        (GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => {
+            over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output)
+        },
+        _ => false,
+    }
+}
+
+pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool {
+    match (l, r) {
+        (AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r),
+        (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_constraint(l, r),
+        _ => false,
+    }
+}
+
+pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool {
+    match (l, r) {
+        (GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident),
+        (GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r),
+        (GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value),
+        _ => false,
+    }
+}
+
+pub fn eq_expr_opt(l: &Option<P<Expr>>, r: &Option<P<Expr>>) -> bool {
+    both(l, r, |l, r| eq_expr(l, r))
+}
+
+pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool {
+    match (l, r) {
+        (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb),
+        (StructRest::Rest(_), StructRest::Rest(_)) | (StructRest::None, StructRest::None) => true,
+        _ => false,
+    }
+}
+
+pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
+    use ExprKind::*;
+    if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) {
+        return false;
+    }
+    match (&l.kind, &r.kind) {
+        (Paren(l), _) => eq_expr(l, r),
+        (_, Paren(r)) => eq_expr(l, r),
+        (Err, Err) => true,
+        (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r),
+        (Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)),
+        (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value),
+        (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
+        (MethodCall(lc, la, _), MethodCall(rc, ra, _)) => eq_path_seg(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
+        (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr),
+        (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
+        (Lit(l), Lit(r)) => l.kind == r.kind,
+        (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
+        (Let(lp, le), Let(rp, re)) => eq_pat(lp, rp) && eq_expr(le, re),
+        (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re),
+        (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt),
+        (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => {
+            eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt)
+        },
+        (Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt),
+        (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb),
+        (TryBlock(l), TryBlock(r)) => eq_block(l, r),
+        (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r),
+        (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re),
+        (Continue(ll), Continue(rl)) => eq_label(ll, rl),
+        (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2),
+        (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
+        (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
+        (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, |l, r| eq_arm(l, r)),
+        (Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => {
+            lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb)
+        },
+        (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb),
+        (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
+        (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
+        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
+        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
+        (Struct(lp, lfs, lb), Struct(rp, rfs, rb)) => {
+            eq_path(lp, rp) && eq_struct_rest(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r))
+        },
+        _ => false,
+    }
+}
+
+pub fn eq_field(l: &Field, r: &Field) -> bool {
+    l.is_placeholder == r.is_placeholder
+        && eq_id(l.ident, r.ident)
+        && eq_expr(&l.expr, &r.expr)
+        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+}
+
+pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
+    l.is_placeholder == r.is_placeholder
+        && eq_pat(&l.pat, &r.pat)
+        && eq_expr(&l.body, &r.body)
+        && eq_expr_opt(&l.guard, &r.guard)
+        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+}
+
+pub fn eq_label(l: &Option<Label>, r: &Option<Label>) -> bool {
+    both(l, r, |l, r| eq_id(l.ident, r.ident))
+}
+
+pub fn eq_block(l: &Block, r: &Block) -> bool {
+    l.rules == r.rules && over(&l.stmts, &r.stmts, |l, r| eq_stmt(l, r))
+}
+
+pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
+    use StmtKind::*;
+    match (&l.kind, &r.kind) {
+        (Local(l), Local(r)) => {
+            eq_pat(&l.pat, &r.pat)
+                && both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
+                && eq_expr_opt(&l.init, &r.init)
+                && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+        },
+        (Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
+        (Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r),
+        (Empty, Empty) => true,
+        (MacCall(l), MacCall(r)) => {
+            l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+        },
+        _ => false,
+    }
+}
+
+pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool {
+    eq_id(l.ident, r.ident)
+        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+        && eq_vis(&l.vis, &r.vis)
+        && eq_kind(&l.kind, &r.kind)
+}
+
+pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
+    use ItemKind::*;
+    match (l, r) {
+        (ExternCrate(l), ExternCrate(r)) => l == r,
+        (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 FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, 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)) => {
+            lu == ru
+                && match (lmk, rmk) {
+                    (ModKind::Loaded(litems, linline, _), ModKind::Loaded(ritems, rinline, _)) => {
+                        linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind))
+                    },
+                    (ModKind::Unloaded, ModKind::Unloaded) => true,
+                    _ => false,
+                }
+        },
+        (ForeignMod(l), ForeignMod(r)) => {
+            both(&l.abi, &r.abi, |l, r| eq_str_lit(l, r))
+                && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
+        },
+        (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
+            eq_defaultness(*ld, *rd)
+                && eq_generics(lg, rg)
+                && over(lb, rb, |l, r| eq_generic_bound(l, r))
+                && both(lt, rt, |l, r| eq_ty(l, r))
+        },
+        (Enum(le, lg), Enum(re, rg)) => {
+            over(&le.variants, &re.variants, |l, r| eq_variant(l, r)) && eq_generics(lg, rg)
+        },
+        (Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => {
+            eq_variant_data(lv, rv) && eq_generics(lg, rg)
+        },
+        (Trait(box TraitKind(la, lu, lg, lb, li)), Trait(box TraitKind(ra, ru, rg, rb, ri))) => {
+            la == ra
+                && matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
+                && eq_generics(lg, rg)
+                && over(lb, rb, |l, r| eq_generic_bound(l, r))
+                && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
+        },
+        (TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, |l, r| eq_generic_bound(l, r)),
+        (
+            Impl(box ImplKind {
+                unsafety: lu,
+                polarity: lp,
+                defaultness: ld,
+                constness: lc,
+                generics: lg,
+                of_trait: lot,
+                self_ty: lst,
+                items: li,
+            }),
+            Impl(box ImplKind {
+                unsafety: ru,
+                polarity: rp,
+                defaultness: rd,
+                constness: rc,
+                generics: rg,
+                of_trait: rot,
+                self_ty: rst,
+                items: ri,
+            }),
+        ) => {
+            matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
+                && matches!(lp, ImplPolarity::Positive) == matches!(rp, ImplPolarity::Positive)
+                && eq_defaultness(*ld, *rd)
+                && matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No)
+                && eq_generics(lg, rg)
+                && both(lot, rot, |l, r| eq_path(&l.path, &r.path))
+                && eq_ty(lst, rst)
+                && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
+        },
+        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
+        (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_mac_args(&l.body, &r.body),
+        _ => false,
+    }
+}
+
+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 FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, 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 TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
+            eq_defaultness(*ld, *rd)
+                && eq_generics(lg, rg)
+                && over(lb, rb, |l, r| eq_generic_bound(l, r))
+                && both(lt, rt, |l, r| eq_ty(l, r))
+        },
+        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
+        _ => false,
+    }
+}
+
+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 FnKind(ld, lf, lg, lb)), Fn(box FnKind(rd, rf, rg, 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 TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
+            eq_defaultness(*ld, *rd)
+                && eq_generics(lg, rg)
+                && over(lb, rb, |l, r| eq_generic_bound(l, r))
+                && both(lt, rt, |l, r| eq_ty(l, r))
+        },
+        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
+        _ => false,
+    }
+}
+
+pub fn eq_variant(l: &Variant, r: &Variant) -> bool {
+    l.is_placeholder == r.is_placeholder
+        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+        && eq_vis(&l.vis, &r.vis)
+        && eq_id(l.ident, r.ident)
+        && eq_variant_data(&l.data, &r.data)
+        && both(&l.disr_expr, &r.disr_expr, |l, r| eq_expr(&l.value, &r.value))
+}
+
+pub fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool {
+    use VariantData::*;
+    match (l, r) {
+        (Unit(_), Unit(_)) => true,
+        (Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, |l, r| eq_struct_field(l, r)),
+        _ => false,
+    }
+}
+
+pub fn eq_struct_field(l: &StructField, r: &StructField) -> bool {
+    l.is_placeholder == r.is_placeholder
+        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+        && eq_vis(&l.vis, &r.vis)
+        && both(&l.ident, &r.ident, |l, r| eq_id(*l, *r))
+        && eq_ty(&l.ty, &r.ty)
+}
+
+pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool {
+    eq_fn_decl(&l.decl, &r.decl) && eq_fn_header(&l.header, &r.header)
+}
+
+pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
+    matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No)
+        && l.asyncness.is_async() == r.asyncness.is_async()
+        && matches!(l.constness, Const::No) == matches!(r.constness, Const::No)
+        && eq_ext(&l.ext, &r.ext)
+}
+
+pub fn eq_generics(l: &Generics, r: &Generics) -> bool {
+    over(&l.params, &r.params, |l, r| eq_generic_param(l, r))
+        && over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| {
+            eq_where_predicate(l, r)
+        })
+}
+
+pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool {
+    use WherePredicate::*;
+    match (l, r) {
+        (BoundPredicate(l), BoundPredicate(r)) => {
+            over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
+                eq_generic_param(l, r)
+            }) && eq_ty(&l.bounded_ty, &r.bounded_ty)
+                && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
+        },
+        (RegionPredicate(l), RegionPredicate(r)) => {
+            eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
+        },
+        (EqPredicate(l), EqPredicate(r)) => eq_ty(&l.lhs_ty, &r.lhs_ty) && eq_ty(&l.rhs_ty, &r.rhs_ty),
+        _ => false,
+    }
+}
+
+pub fn eq_use_tree(l: &UseTree, r: &UseTree) -> bool {
+    eq_path(&l.prefix, &r.prefix) && eq_use_tree_kind(&l.kind, &r.kind)
+}
+
+pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
+    use UseTreeKind::*;
+    match (l, r) {
+        (Glob, Glob) => true,
+        (Simple(l, _, _), Simple(r, _, _)) => both(l, r, |l, r| eq_id(*l, *r)),
+        (Nested(l), Nested(r)) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)),
+        _ => false,
+    }
+}
+
+pub fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool {
+    eq_expr(&l.value, &r.value)
+}
+
+pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
+    matches!(
+        (l, r),
+        (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_))
+    )
+}
+
+pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool {
+    use VisibilityKind::*;
+    match (&l.kind, &r.kind) {
+        (Public, Public) | (Inherited, Inherited) | (Crate(_), Crate(_)) => true,
+        (Restricted { path: l, .. }, Restricted { path: r, .. }) => eq_path(l, r),
+        _ => false,
+    }
+}
+
+pub fn eq_fn_decl(l: &FnDecl, r: &FnDecl) -> bool {
+    eq_fn_ret_ty(&l.output, &r.output)
+        && over(&l.inputs, &r.inputs, |l, r| {
+            l.is_placeholder == r.is_placeholder
+                && eq_pat(&l.pat, &r.pat)
+                && eq_ty(&l.ty, &r.ty)
+                && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+        })
+}
+
+pub fn eq_fn_ret_ty(l: &FnRetTy, r: &FnRetTy) -> bool {
+    match (l, r) {
+        (FnRetTy::Default(_), FnRetTy::Default(_)) => true,
+        (FnRetTy::Ty(l), FnRetTy::Ty(r)) => eq_ty(l, r),
+        _ => false,
+    }
+}
+
+pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
+    use TyKind::*;
+    match (&l.kind, &r.kind) {
+        (Paren(l), _) => eq_ty(l, r),
+        (_, Paren(r)) => eq_ty(l, r),
+        (Never, Never) | (Infer, Infer) | (ImplicitSelf, ImplicitSelf) | (Err, Err) | (CVarArgs, CVarArgs) => true,
+        (Slice(l), Slice(r)) => eq_ty(l, r),
+        (Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value),
+        (Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty),
+        (Rptr(ll, l), Rptr(rl, r)) => {
+            both(ll, rl, |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty)
+        },
+        (BareFn(l), BareFn(r)) => {
+            l.unsafety == r.unsafety
+                && eq_ext(&l.ext, &r.ext)
+                && over(&l.generic_params, &r.generic_params, |l, r| eq_generic_param(l, r))
+                && eq_fn_decl(&l.decl, &r.decl)
+        },
+        (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
+        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
+        (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, |l, r| eq_generic_bound(l, r)),
+        (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, |l, r| eq_generic_bound(l, r)),
+        (Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
+        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
+        _ => false,
+    }
+}
+
+pub fn eq_ext(l: &Extern, r: &Extern) -> bool {
+    use Extern::*;
+    match (l, r) {
+        (None, None) | (Implicit, Implicit) => true,
+        (Explicit(l), Explicit(r)) => eq_str_lit(l, r),
+        _ => false,
+    }
+}
+
+pub fn eq_str_lit(l: &StrLit, r: &StrLit) -> bool {
+    l.style == r.style && l.symbol == r.symbol && l.suffix == r.suffix
+}
+
+pub fn eq_poly_ref_trait(l: &PolyTraitRef, r: &PolyTraitRef) -> bool {
+    eq_path(&l.trait_ref.path, &r.trait_ref.path)
+        && over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
+            eq_generic_param(l, r)
+        })
+}
+
+pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
+    use GenericParamKind::*;
+    l.is_placeholder == r.is_placeholder
+        && eq_id(l.ident, r.ident)
+        && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
+        && match (&l.kind, &r.kind) {
+            (Lifetime, Lifetime) => true,
+            (Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)),
+            (
+                Const {
+                    ty: lt,
+                    kw_span: _,
+                    default: ld,
+                },
+                Const {
+                    ty: rt,
+                    kw_span: _,
+                    default: rd,
+                },
+            ) => eq_ty(lt, rt) && both(ld, rd, |ld, rd| eq_anon_const(ld, rd)),
+            _ => false,
+        }
+        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+}
+
+pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool {
+    use GenericBound::*;
+    match (l, r) {
+        (Trait(ptr1, tbm1), Trait(ptr2, tbm2)) => tbm1 == tbm2 && eq_poly_ref_trait(ptr1, ptr2),
+        (Outlives(l), Outlives(r)) => eq_id(l.ident, r.ident),
+        _ => false,
+    }
+}
+
+pub fn eq_assoc_constraint(l: &AssocTyConstraint, r: &AssocTyConstraint) -> bool {
+    use AssocTyConstraintKind::*;
+    eq_id(l.ident, r.ident)
+        && match (&l.kind, &r.kind) {
+            (Equality { ty: l }, Equality { ty: r }) => eq_ty(l, r),
+            (Bound { bounds: l }, Bound { bounds: r }) => over(l, r, |l, r| eq_generic_bound(l, r)),
+            _ => false,
+        }
+}
+
+pub fn eq_mac_call(l: &MacCall, r: &MacCall) -> bool {
+    eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args)
+}
+
+pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
+    use AttrKind::*;
+    l.style == r.style
+        && match (&l.kind, &r.kind) {
+            (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
+            (Normal(l, _), Normal(r, _)) => eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args),
+            _ => false,
+        }
+}
+
+pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
+    use MacArgs::*;
+    match (l, r) {
+        (Empty, Empty) => true,
+        (Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts),
+        (Eq(_, lt), Eq(_, rt)) => lt.kind == rt.kind,
+        _ => false,
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs b/src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs
new file mode 100644 (file)
index 0000000..eefcbab
--- /dev/null
@@ -0,0 +1,45 @@
+use core::iter::FusedIterator;
+use rustc_ast::visit::{walk_attribute, walk_expr, Visitor};
+use rustc_ast::{Attribute, Expr};
+use rustc_span::symbol::Ident;
+
+pub struct IdentIter(std::vec::IntoIter<Ident>);
+
+impl Iterator for IdentIter {
+    type Item = Ident;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.0.next()
+    }
+}
+
+impl FusedIterator for IdentIter {}
+
+impl From<&Expr> for IdentIter {
+    fn from(expr: &Expr) -> Self {
+        let mut visitor = IdentCollector::default();
+
+        walk_expr(&mut visitor, expr);
+
+        IdentIter(visitor.0.into_iter())
+    }
+}
+
+impl From<&Attribute> for IdentIter {
+    fn from(attr: &Attribute) -> Self {
+        let mut visitor = IdentCollector::default();
+
+        walk_attribute(&mut visitor, attr);
+
+        IdentIter(visitor.0.into_iter())
+    }
+}
+
+#[derive(Default)]
+struct IdentCollector(Vec<Ident>);
+
+impl Visitor<'_> for IdentCollector {
+    fn visit_ident(&mut self, ident: Ident) {
+        self.0.push(ident);
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs
new file mode 100644 (file)
index 0000000..8d28421
--- /dev/null
@@ -0,0 +1,150 @@
+use rustc_ast::ast;
+use rustc_errors::Applicability;
+use rustc_session::Session;
+use rustc_span::sym;
+use std::str::FromStr;
+
+/// Deprecation status of attributes known by Clippy.
+#[allow(dead_code)]
+pub enum DeprecationStatus {
+    /// Attribute is deprecated
+    Deprecated,
+    /// Attribute is deprecated and was replaced by the named attribute
+    Replaced(&'static str),
+    None,
+}
+
+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),
+];
+
+pub struct LimitStack {
+    stack: Vec<u64>,
+}
+
+impl Drop for LimitStack {
+    fn drop(&mut self) {
+        assert_eq!(self.stack.len(), 1);
+    }
+}
+
+impl LimitStack {
+    #[must_use]
+    pub fn new(limit: u64) -> Self {
+        Self { stack: vec![limit] }
+    }
+    pub fn limit(&self) -> u64 {
+        *self.stack.last().expect("there should always be a value in the stack")
+    }
+    pub fn push_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
+        let stack = &mut self.stack;
+        parse_attrs(sess, attrs, name, |val| stack.push(val));
+    }
+    pub fn pop_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
+        let stack = &mut self.stack;
+        parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val)));
+    }
+}
+
+pub fn get_attr<'a>(
+    sess: &'a Session,
+    attrs: &'a [ast::Attribute],
+    name: &'static str,
+) -> impl Iterator<Item = &'a ast::Attribute> {
+    attrs.iter().filter(move |attr| {
+        let attr = if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
+            attr
+        } else {
+            return false;
+        };
+        let attr_segments = &attr.path.segments;
+        if attr_segments.len() == 2 && attr_segments[0].ident.name == sym::clippy {
+            BUILTIN_ATTRIBUTES
+                .iter()
+                .find_map(|&(builtin_name, ref deprecation_status)| {
+                    if attr_segments[1].ident.name.as_str() == builtin_name {
+                        Some(deprecation_status)
+                    } else {
+                        None
+                    }
+                })
+                .map_or_else(
+                    || {
+                        sess.span_err(attr_segments[1].ident.span, "usage of unknown attribute");
+                        false
+                    },
+                    |deprecation_status| {
+                        let mut diag =
+                            sess.struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute");
+                        match *deprecation_status {
+                            DeprecationStatus::Deprecated => {
+                                diag.emit();
+                                false
+                            },
+                            DeprecationStatus::Replaced(new_name) => {
+                                diag.span_suggestion(
+                                    attr_segments[1].ident.span,
+                                    "consider using",
+                                    new_name.to_string(),
+                                    Applicability::MachineApplicable,
+                                );
+                                diag.emit();
+                                false
+                            },
+                            DeprecationStatus::None => {
+                                diag.cancel();
+                                attr_segments[1].ident.name.as_str() == name
+                            },
+                        }
+                    },
+                )
+        } else {
+            false
+        }
+    })
+}
+
+fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'static str, mut f: F) {
+    for attr in get_attr(sess, attrs, name) {
+        if let Some(ref value) = attr.value_str() {
+            if let Ok(value) = FromStr::from_str(&value.as_str()) {
+                f(value)
+            } else {
+                sess.span_err(attr.span, "not a number");
+            }
+        } else {
+            sess.span_err(attr.span, "bad clippy attribute");
+        }
+    }
+}
+
+pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
+    let mut unique_attr = None;
+    for attr in get_attr(sess, attrs, name) {
+        match attr.style {
+            ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()),
+            ast::AttrStyle::Inner => {
+                sess.struct_span_err(attr.span, &format!("`{}` is defined multiple times", name))
+                    .span_note(unique_attr.as_ref().unwrap().span, "first definition found here")
+                    .emit();
+            },
+            ast::AttrStyle::Outer => {
+                sess.span_err(attr.span, &format!("`{}` cannot be an outer attribute", name));
+            },
+        }
+    }
+    unique_attr
+}
+
+/// Return true if the attributes contain any of `proc_macro`,
+/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
+pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
+    attrs.iter().any(|attr| sess.is_proc_macro_attr(attr))
+}
diff --git a/src/tools/clippy/clippy_utils/src/camel_case.rs b/src/tools/clippy/clippy_utils/src/camel_case.rs
new file mode 100644 (file)
index 0000000..ba1c01e
--- /dev/null
@@ -0,0 +1,121 @@
+/// Returns the index of the character after the first camel-case component of `s`.
+#[must_use]
+pub fn until(s: &str) -> usize {
+    let mut iter = s.char_indices();
+    if let Some((_, first)) = iter.next() {
+        if !first.is_uppercase() {
+            return 0;
+        }
+    } else {
+        return 0;
+    }
+    let mut up = true;
+    let mut last_i = 0;
+    for (i, c) in iter {
+        if up {
+            if c.is_lowercase() {
+                up = false;
+            } else {
+                return last_i;
+            }
+        } else if c.is_uppercase() {
+            up = true;
+            last_i = i;
+        } else if !c.is_lowercase() {
+            return i;
+        }
+    }
+    if up {
+        last_i
+    } else {
+        s.len()
+    }
+}
+
+/// Returns index of the last camel-case component of `s`.
+#[must_use]
+pub fn from(s: &str) -> usize {
+    let mut iter = s.char_indices().rev();
+    if let Some((_, first)) = iter.next() {
+        if !first.is_lowercase() {
+            return s.len();
+        }
+    } else {
+        return s.len();
+    }
+    let mut down = true;
+    let mut last_i = s.len();
+    for (i, c) in iter {
+        if down {
+            if c.is_uppercase() {
+                down = false;
+                last_i = i;
+            } else if !c.is_lowercase() {
+                return last_i;
+            }
+        } else if c.is_lowercase() {
+            down = true;
+        } else if c.is_uppercase() {
+            last_i = i;
+        } else {
+            return last_i;
+        }
+    }
+    last_i
+}
+
+#[cfg(test)]
+mod test {
+    use super::{from, until};
+
+    #[test]
+    fn from_full() {
+        assert_eq!(from("AbcDef"), 0);
+        assert_eq!(from("Abc"), 0);
+        assert_eq!(from("ABcd"), 0);
+        assert_eq!(from("ABcdEf"), 0);
+        assert_eq!(from("AabABcd"), 0);
+    }
+
+    #[test]
+    fn from_partial() {
+        assert_eq!(from("abcDef"), 3);
+        assert_eq!(from("aDbc"), 1);
+        assert_eq!(from("aabABcd"), 3);
+    }
+
+    #[test]
+    fn from_not() {
+        assert_eq!(from("AbcDef_"), 7);
+        assert_eq!(from("AbcDD"), 5);
+    }
+
+    #[test]
+    fn from_caps() {
+        assert_eq!(from("ABCD"), 4);
+    }
+
+    #[test]
+    fn until_full() {
+        assert_eq!(until("AbcDef"), 6);
+        assert_eq!(until("Abc"), 3);
+    }
+
+    #[test]
+    fn until_not() {
+        assert_eq!(until("abcDef"), 0);
+        assert_eq!(until("aDbc"), 0);
+    }
+
+    #[test]
+    fn until_partial() {
+        assert_eq!(until("AbcDef_"), 6);
+        assert_eq!(until("CallTypeC"), 8);
+        assert_eq!(until("AbcDD"), 3);
+    }
+
+    #[test]
+    fn until_caps() {
+        assert_eq!(until("ABCD"), 0);
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/comparisons.rs b/src/tools/clippy/clippy_utils/src/comparisons.rs
new file mode 100644 (file)
index 0000000..7a18d5e
--- /dev/null
@@ -0,0 +1,36 @@
+//! Utility functions about comparison operators.
+
+#![deny(clippy::missing_docs_in_private_items)]
+
+use rustc_hir::{BinOpKind, Expr};
+
+#[derive(PartialEq, Eq, Debug, Copy, Clone)]
+/// Represent a normalized comparison operator.
+pub enum Rel {
+    /// `<`
+    Lt,
+    /// `<=`
+    Le,
+    /// `==`
+    Eq,
+    /// `!=`
+    Ne,
+}
+
+/// Put the expression in the form  `lhs < rhs`, `lhs <= rhs`, `lhs == rhs` or
+/// `lhs != rhs`.
+pub fn normalize_comparison<'a>(
+    op: BinOpKind,
+    lhs: &'a Expr<'a>,
+    rhs: &'a Expr<'a>,
+) -> Option<(Rel, &'a Expr<'a>, &'a Expr<'a>)> {
+    match op {
+        BinOpKind::Lt => Some((Rel::Lt, lhs, rhs)),
+        BinOpKind::Le => Some((Rel::Le, lhs, rhs)),
+        BinOpKind::Gt => Some((Rel::Lt, rhs, lhs)),
+        BinOpKind::Ge => Some((Rel::Le, rhs, lhs)),
+        BinOpKind::Eq => Some((Rel::Eq, rhs, lhs)),
+        BinOpKind::Ne => Some((Rel::Ne, rhs, lhs)),
+        _ => None,
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
new file mode 100644 (file)
index 0000000..802c010
--- /dev/null
@@ -0,0 +1,574 @@
+#![allow(clippy::float_cmp)]
+
+use crate::{clip, sext, unsext};
+use if_chain::if_chain;
+use rustc_ast::ast::{self, LitFloatType, LitKind};
+use rustc_data_structures::sync::Lrc;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
+use rustc_lint::LateContext;
+use rustc_middle::mir::interpret::Scalar;
+use rustc_middle::ty::subst::{Subst, SubstsRef};
+use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt};
+use rustc_middle::{bug, span_bug};
+use rustc_span::symbol::Symbol;
+use std::cmp::Ordering::{self, Equal};
+use std::convert::TryInto;
+use std::hash::{Hash, Hasher};
+
+/// A `LitKind`-like enum to fold constant `Expr`s into.
+#[derive(Debug, Clone)]
+pub enum Constant {
+    /// A `String` (e.g., "abc").
+    Str(String),
+    /// A binary string (e.g., `b"abc"`).
+    Binary(Lrc<[u8]>),
+    /// A single `char` (e.g., `'a'`).
+    Char(char),
+    /// An integer's bit representation.
+    Int(u128),
+    /// An `f32`.
+    F32(f32),
+    /// An `f64`.
+    F64(f64),
+    /// `true` or `false`.
+    Bool(bool),
+    /// An array of constants.
+    Vec(Vec<Constant>),
+    /// Also an array, but with only one constant, repeated N times.
+    Repeat(Box<Constant>, u64),
+    /// A tuple of constants.
+    Tuple(Vec<Constant>),
+    /// A raw pointer.
+    RawPtr(u128),
+    /// A reference
+    Ref(Box<Constant>),
+    /// A literal with syntax error.
+    Err(Symbol),
+}
+
+impl PartialEq for Constant {
+    fn eq(&self, other: &Self) -> bool {
+        match (self, other) {
+            (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
+            (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
+            (&Self::Char(l), &Self::Char(r)) => l == r,
+            (&Self::Int(l), &Self::Int(r)) => l == r,
+            (&Self::F64(l), &Self::F64(r)) => {
+                // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
+                // `Fw32 == Fw64`, so don’t compare them.
+                // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
+                l.to_bits() == r.to_bits()
+            },
+            (&Self::F32(l), &Self::F32(r)) => {
+                // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
+                // `Fw32 == Fw64`, so don’t compare them.
+                // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
+                f64::from(l).to_bits() == f64::from(r).to_bits()
+            },
+            (&Self::Bool(l), &Self::Bool(r)) => l == r,
+            (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
+            (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
+            (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb,
+            // TODO: are there inter-type equalities?
+            _ => false,
+        }
+    }
+}
+
+impl Hash for Constant {
+    fn hash<H>(&self, state: &mut H)
+    where
+        H: Hasher,
+    {
+        std::mem::discriminant(self).hash(state);
+        match *self {
+            Self::Str(ref s) => {
+                s.hash(state);
+            },
+            Self::Binary(ref b) => {
+                b.hash(state);
+            },
+            Self::Char(c) => {
+                c.hash(state);
+            },
+            Self::Int(i) => {
+                i.hash(state);
+            },
+            Self::F32(f) => {
+                f64::from(f).to_bits().hash(state);
+            },
+            Self::F64(f) => {
+                f.to_bits().hash(state);
+            },
+            Self::Bool(b) => {
+                b.hash(state);
+            },
+            Self::Vec(ref v) | Self::Tuple(ref v) => {
+                v.hash(state);
+            },
+            Self::Repeat(ref c, l) => {
+                c.hash(state);
+                l.hash(state);
+            },
+            Self::RawPtr(u) => {
+                u.hash(state);
+            },
+            Self::Ref(ref r) => {
+                r.hash(state);
+            },
+            Self::Err(ref s) => {
+                s.hash(state);
+            },
+        }
+    }
+}
+
+impl Constant {
+    pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
+        match (left, right) {
+            (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
+            (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
+            (&Self::Int(l), &Self::Int(r)) => {
+                if let ty::Int(int_ty) = *cmp_type.kind() {
+                    Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
+                } else {
+                    Some(l.cmp(&r))
+                }
+            },
+            (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
+            (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
+            (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
+            (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => l
+                .iter()
+                .zip(r.iter())
+                .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
+                .find(|r| r.map_or(true, |o| o != Ordering::Equal))
+                .unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
+            (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
+                match Self::partial_cmp(tcx, cmp_type, lv, rv) {
+                    Some(Equal) => Some(ls.cmp(rs)),
+                    x => x,
+                }
+            },
+            (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb),
+            // TODO: are there any useful inter-type orderings?
+            _ => None,
+        }
+    }
+}
+
+/// Parses a `LitKind` to a `Constant`.
+pub fn lit_to_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
+    match *lit {
+        LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
+        LitKind::Byte(b) => Constant::Int(u128::from(b)),
+        LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)),
+        LitKind::Char(c) => Constant::Char(c),
+        LitKind::Int(n, _) => Constant::Int(n),
+        LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
+            ast::FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()),
+            ast::FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()),
+        },
+        LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() {
+            ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
+            ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
+            _ => bug!(),
+        },
+        LitKind::Bool(b) => Constant::Bool(b),
+        LitKind::Err(s) => Constant::Err(s),
+    }
+}
+
+pub fn constant<'tcx>(
+    lcx: &LateContext<'tcx>,
+    typeck_results: &ty::TypeckResults<'tcx>,
+    e: &Expr<'_>,
+) -> Option<(Constant, bool)> {
+    let mut cx = ConstEvalLateContext {
+        lcx,
+        typeck_results,
+        param_env: lcx.param_env,
+        needed_resolution: false,
+        substs: lcx.tcx.intern_substs(&[]),
+    };
+    cx.expr(e).map(|cst| (cst, cx.needed_resolution))
+}
+
+pub fn constant_simple<'tcx>(
+    lcx: &LateContext<'tcx>,
+    typeck_results: &ty::TypeckResults<'tcx>,
+    e: &Expr<'_>,
+) -> Option<Constant> {
+    constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
+}
+
+/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`.
+pub fn constant_context<'a, 'tcx>(
+    lcx: &'a LateContext<'tcx>,
+    typeck_results: &'a ty::TypeckResults<'tcx>,
+) -> ConstEvalLateContext<'a, 'tcx> {
+    ConstEvalLateContext {
+        lcx,
+        typeck_results,
+        param_env: lcx.param_env,
+        needed_resolution: false,
+        substs: lcx.tcx.intern_substs(&[]),
+    }
+}
+
+pub struct ConstEvalLateContext<'a, 'tcx> {
+    lcx: &'a LateContext<'tcx>,
+    typeck_results: &'a ty::TypeckResults<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    needed_resolution: bool,
+    substs: SubstsRef<'tcx>,
+}
+
+impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
+    /// Simple constant folding: Insert an expression, get a constant or none.
+    pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
+        match e.kind {
+            ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
+            ExprKind::Block(ref block, _) => self.block(block),
+            ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e))),
+            ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec),
+            ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple),
+            ExprKind::Repeat(ref value, _) => {
+                let n = match self.typeck_results.expr_ty(e).kind() {
+                    ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?,
+                    _ => span_bug!(e.span, "typeck error"),
+                };
+                self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
+            },
+            ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op {
+                UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)),
+                UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
+                UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
+            }),
+            ExprKind::If(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
+            ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right),
+            ExprKind::Call(ref callee, ref args) => {
+                // We only handle a few const functions for now.
+                if_chain! {
+                    if args.is_empty();
+                    if let ExprKind::Path(qpath) = &callee.kind;
+                    let res = self.typeck_results.qpath_res(qpath, callee.hir_id);
+                    if let Some(def_id) = res.opt_def_id();
+                    let def_path: Vec<_> = self.lcx.get_def_path(def_id).into_iter().map(Symbol::as_str).collect();
+                    let def_path: Vec<&str> = def_path.iter().take(4).map(|s| &**s).collect();
+                    if let ["core", "num", int_impl, "max_value"] = *def_path;
+                    then {
+                       let value = match int_impl {
+                           "<impl i8>" => i8::MAX as u128,
+                           "<impl i16>" => i16::MAX as u128,
+                           "<impl i32>" => i32::MAX as u128,
+                           "<impl i64>" => i64::MAX as u128,
+                           "<impl i128>" => i128::MAX as u128,
+                           _ => return None,
+                       };
+                       Some(Constant::Int(value))
+                    }
+                    else {
+                        None
+                    }
+                }
+            },
+            ExprKind::Index(ref arr, ref index) => self.index(arr, index),
+            ExprKind::AddrOf(_, _, ref inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
+            // TODO: add other expressions.
+            _ => None,
+        }
+    }
+
+    #[allow(clippy::cast_possible_wrap)]
+    fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
+        use self::Constant::{Bool, Int};
+        match *o {
+            Bool(b) => Some(Bool(!b)),
+            Int(value) => {
+                let value = !value;
+                match *ty.kind() {
+                    ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
+                    ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
+                    _ => None,
+                }
+            },
+            _ => None,
+        }
+    }
+
+    fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
+        use self::Constant::{Int, F32, F64};
+        match *o {
+            Int(value) => {
+                let ity = match *ty.kind() {
+                    ty::Int(ity) => ity,
+                    _ => return None,
+                };
+                // sign extend
+                let value = sext(self.lcx.tcx, value, ity);
+                let value = value.checked_neg()?;
+                // clear unused bits
+                Some(Int(unsext(self.lcx.tcx, value, ity)))
+            },
+            F32(f) => Some(F32(-f)),
+            F64(f) => Some(F64(-f)),
+            _ => None,
+        }
+    }
+
+    /// Create `Some(Vec![..])` of all constants, unless there is any
+    /// non-constant part.
+    fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
+        vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
+    }
+
+    /// Lookup a possibly constant expression from a `ExprKind::Path`.
+    fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant> {
+        let res = self.typeck_results.qpath_res(qpath, id);
+        match res {
+            Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
+                let substs = self.typeck_results.node_substs(id);
+                let substs = if self.substs.is_empty() {
+                    substs
+                } else {
+                    substs.subst(self.lcx.tcx, self.substs)
+                };
+
+                let result = self
+                    .lcx
+                    .tcx
+                    .const_eval_resolve(
+                        self.param_env,
+                        ty::WithOptConstParam::unknown(def_id),
+                        substs,
+                        None,
+                        None,
+                    )
+                    .ok()
+                    .map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?;
+                let result = miri_to_const(&result);
+                if result.is_some() {
+                    self.needed_resolution = true;
+                }
+                result
+            },
+            // FIXME: cover all usable cases.
+            _ => None,
+        }
+    }
+
+    fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant> {
+        let lhs = self.expr(lhs);
+        let index = self.expr(index);
+
+        match (lhs, index) {
+            (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) {
+                Some(Constant::F32(x)) => Some(Constant::F32(*x)),
+                Some(Constant::F64(x)) => Some(Constant::F64(*x)),
+                _ => None,
+            },
+            (Some(Constant::Vec(vec)), _) => {
+                if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) {
+                    match vec.get(0) {
+                        Some(Constant::F32(x)) => Some(Constant::F32(*x)),
+                        Some(Constant::F64(x)) => Some(Constant::F64(*x)),
+                        _ => None,
+                    }
+                } else {
+                    None
+                }
+            },
+            _ => None,
+        }
+    }
+
+    /// A block can only yield a constant if it only has one constant expression.
+    fn block(&mut self, block: &Block<'_>) -> Option<Constant> {
+        if block.stmts.is_empty() {
+            block.expr.as_ref().and_then(|b| self.expr(b))
+        } else {
+            None
+        }
+    }
+
+    fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant> {
+        if let Some(Constant::Bool(b)) = self.expr(cond) {
+            if b {
+                self.expr(&*then)
+            } else {
+                otherwise.as_ref().and_then(|expr| self.expr(expr))
+            }
+        } else {
+            None
+        }
+    }
+
+    fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
+        let l = self.expr(left)?;
+        let r = self.expr(right);
+        match (l, r) {
+            (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() {
+                ty::Int(ity) => {
+                    let l = sext(self.lcx.tcx, l, ity);
+                    let r = sext(self.lcx.tcx, r, ity);
+                    let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity));
+                    match op.node {
+                        BinOpKind::Add => l.checked_add(r).map(zext),
+                        BinOpKind::Sub => l.checked_sub(r).map(zext),
+                        BinOpKind::Mul => l.checked_mul(r).map(zext),
+                        BinOpKind::Div if r != 0 => l.checked_div(r).map(zext),
+                        BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext),
+                        BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext),
+                        BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext),
+                        BinOpKind::BitXor => Some(zext(l ^ r)),
+                        BinOpKind::BitOr => Some(zext(l | r)),
+                        BinOpKind::BitAnd => Some(zext(l & r)),
+                        BinOpKind::Eq => Some(Constant::Bool(l == r)),
+                        BinOpKind::Ne => Some(Constant::Bool(l != r)),
+                        BinOpKind::Lt => Some(Constant::Bool(l < r)),
+                        BinOpKind::Le => Some(Constant::Bool(l <= r)),
+                        BinOpKind::Ge => Some(Constant::Bool(l >= r)),
+                        BinOpKind::Gt => Some(Constant::Bool(l > r)),
+                        _ => None,
+                    }
+                },
+                ty::Uint(_) => match op.node {
+                    BinOpKind::Add => l.checked_add(r).map(Constant::Int),
+                    BinOpKind::Sub => l.checked_sub(r).map(Constant::Int),
+                    BinOpKind::Mul => l.checked_mul(r).map(Constant::Int),
+                    BinOpKind::Div => l.checked_div(r).map(Constant::Int),
+                    BinOpKind::Rem => l.checked_rem(r).map(Constant::Int),
+                    BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int),
+                    BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int),
+                    BinOpKind::BitXor => Some(Constant::Int(l ^ r)),
+                    BinOpKind::BitOr => Some(Constant::Int(l | r)),
+                    BinOpKind::BitAnd => Some(Constant::Int(l & r)),
+                    BinOpKind::Eq => Some(Constant::Bool(l == r)),
+                    BinOpKind::Ne => Some(Constant::Bool(l != r)),
+                    BinOpKind::Lt => Some(Constant::Bool(l < r)),
+                    BinOpKind::Le => Some(Constant::Bool(l <= r)),
+                    BinOpKind::Ge => Some(Constant::Bool(l >= r)),
+                    BinOpKind::Gt => Some(Constant::Bool(l > r)),
+                    _ => None,
+                },
+                _ => None,
+            },
+            (Constant::F32(l), Some(Constant::F32(r))) => match op.node {
+                BinOpKind::Add => Some(Constant::F32(l + r)),
+                BinOpKind::Sub => Some(Constant::F32(l - r)),
+                BinOpKind::Mul => Some(Constant::F32(l * r)),
+                BinOpKind::Div => Some(Constant::F32(l / r)),
+                BinOpKind::Rem => Some(Constant::F32(l % r)),
+                BinOpKind::Eq => Some(Constant::Bool(l == r)),
+                BinOpKind::Ne => Some(Constant::Bool(l != r)),
+                BinOpKind::Lt => Some(Constant::Bool(l < r)),
+                BinOpKind::Le => Some(Constant::Bool(l <= r)),
+                BinOpKind::Ge => Some(Constant::Bool(l >= r)),
+                BinOpKind::Gt => Some(Constant::Bool(l > r)),
+                _ => None,
+            },
+            (Constant::F64(l), Some(Constant::F64(r))) => match op.node {
+                BinOpKind::Add => Some(Constant::F64(l + r)),
+                BinOpKind::Sub => Some(Constant::F64(l - r)),
+                BinOpKind::Mul => Some(Constant::F64(l * r)),
+                BinOpKind::Div => Some(Constant::F64(l / r)),
+                BinOpKind::Rem => Some(Constant::F64(l % r)),
+                BinOpKind::Eq => Some(Constant::Bool(l == r)),
+                BinOpKind::Ne => Some(Constant::Bool(l != r)),
+                BinOpKind::Lt => Some(Constant::Bool(l < r)),
+                BinOpKind::Le => Some(Constant::Bool(l <= r)),
+                BinOpKind::Ge => Some(Constant::Bool(l >= r)),
+                BinOpKind::Gt => Some(Constant::Bool(l > r)),
+                _ => None,
+            },
+            (l, r) => match (op.node, l, r) {
+                (BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)),
+                (BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)),
+                (BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => {
+                    Some(r)
+                },
+                (BinOpKind::BitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)),
+                (BinOpKind::BitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)),
+                (BinOpKind::BitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)),
+                _ => None,
+            },
+        }
+    }
+}
+
+pub fn miri_to_const(result: &ty::Const<'_>) -> Option<Constant> {
+    use rustc_middle::mir::interpret::ConstValue;
+    match result.val {
+        ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => {
+            match result.ty.kind() {
+                ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
+                ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
+                ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
+                    int.try_into().expect("invalid f32 bit representation"),
+                ))),
+                ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
+                    int.try_into().expect("invalid f64 bit representation"),
+                ))),
+                ty::RawPtr(type_and_mut) => {
+                    if let ty::Uint(_) = type_and_mut.ty.kind() {
+                        return Some(Constant::RawPtr(int.assert_bits(int.size())));
+                    }
+                    None
+                },
+                // FIXME: implement other conversions.
+                _ => None,
+            }
+        },
+        ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind() {
+            ty::Ref(_, tam, _) => match tam.kind() {
+                ty::Str => String::from_utf8(
+                    data.inspect_with_uninit_and_ptr_outside_interpreter(start..end)
+                        .to_owned(),
+                )
+                .ok()
+                .map(Constant::Str),
+                _ => None,
+            },
+            _ => None,
+        },
+        ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind() {
+            ty::Array(sub_type, len) => match sub_type.kind() {
+                ty::Float(FloatTy::F32) => match miri_to_const(len) {
+                    Some(Constant::Int(len)) => alloc
+                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize))
+                        .to_owned()
+                        .chunks(4)
+                        .map(|chunk| {
+                            Some(Constant::F32(f32::from_le_bytes(
+                                chunk.try_into().expect("this shouldn't happen"),
+                            )))
+                        })
+                        .collect::<Option<Vec<Constant>>>()
+                        .map(Constant::Vec),
+                    _ => None,
+                },
+                ty::Float(FloatTy::F64) => match miri_to_const(len) {
+                    Some(Constant::Int(len)) => alloc
+                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize))
+                        .to_owned()
+                        .chunks(8)
+                        .map(|chunk| {
+                            Some(Constant::F64(f64::from_le_bytes(
+                                chunk.try_into().expect("this shouldn't happen"),
+                            )))
+                        })
+                        .collect::<Option<Vec<Constant>>>()
+                        .map(Constant::Vec),
+                    _ => None,
+                },
+                // FIXME: implement other array type conversions.
+                _ => None,
+            },
+            _ => None,
+        },
+        // FIXME: implement other conversions.
+        _ => None,
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs
new file mode 100644 (file)
index 0000000..269be21
--- /dev/null
@@ -0,0 +1,226 @@
+//! Clippy wrappers around rustc's diagnostic functions.
+
+use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_hir::HirId;
+use rustc_lint::{LateContext, Lint, LintContext};
+use rustc_span::source_map::{MultiSpan, Span};
+use std::env;
+
+fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) {
+    if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() {
+        diag.help(&format!(
+            "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}",
+            &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
+                // extract just major + minor version and ignore patch versions
+                format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap())
+            }),
+            lint.name_lower().replacen("clippy::", "", 1)
+        ));
+    }
+}
+
+/// Emit a basic lint message with a `msg` and a `span`.
+///
+/// This is the most primitive of our lint emission methods and can
+/// be a good way to get a new lint started.
+///
+/// Usually it's nicer to provide more context for lint messages.
+/// Be sure the output is understandable when you use this method.
+///
+/// # Example
+///
+/// ```ignore
+/// error: usage of mem::forget on Drop type
+///   --> $DIR/mem_forget.rs:17:5
+///    |
+/// 17 |     std::mem::forget(seven);
+///    |     ^^^^^^^^^^^^^^^^^^^^^^^
+/// ```
+pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<MultiSpan>, msg: &str) {
+    cx.struct_span_lint(lint, sp, |diag| {
+        let mut diag = diag.build(msg);
+        docs_link(&mut diag, lint);
+        diag.emit();
+    });
+}
+
+/// Same as `span_lint` but with an extra `help` message.
+///
+/// Use this if you want to provide some general help but
+/// can't provide a specific machine applicable suggestion.
+///
+/// The `help` message can be optionally attached to a `Span`.
+///
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
+///
+/// # Example
+///
+/// ```ignore
+/// error: constant division of 0.0 with 0.0 will always result in NaN
+///   --> $DIR/zero_div_zero.rs:6:25
+///    |
+/// 6  |     let other_f64_nan = 0.0f64 / 0.0;
+///    |                         ^^^^^^^^^^^^
+///    |
+///    = help: Consider using `f64::NAN` if you would like a constant representing NaN
+/// ```
+pub fn span_lint_and_help<'a, T: LintContext>(
+    cx: &'a T,
+    lint: &'static Lint,
+    span: Span,
+    msg: &str,
+    help_span: Option<Span>,
+    help: &str,
+) {
+    cx.struct_span_lint(lint, span, |diag| {
+        let mut diag = diag.build(msg);
+        if let Some(help_span) = help_span {
+            diag.span_help(help_span, help);
+        } else {
+            diag.help(help);
+        }
+        docs_link(&mut diag, lint);
+        diag.emit();
+    });
+}
+
+/// Like `span_lint` but with a `note` section instead of a `help` message.
+///
+/// The `note` message is presented separately from the main lint message
+/// and is attached to a specific span:
+///
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
+///
+/// # Example
+///
+/// ```ignore
+/// error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
+///   --> $DIR/drop_forget_ref.rs:10:5
+///    |
+/// 10 |     forget(&SomeStruct);
+///    |     ^^^^^^^^^^^^^^^^^^^
+///    |
+///    = note: `-D clippy::forget-ref` implied by `-D warnings`
+/// note: argument has type &SomeStruct
+///   --> $DIR/drop_forget_ref.rs:10:12
+///    |
+/// 10 |     forget(&SomeStruct);
+///    |            ^^^^^^^^^^^
+/// ```
+pub fn span_lint_and_note<'a, T: LintContext>(
+    cx: &'a T,
+    lint: &'static Lint,
+    span: impl Into<MultiSpan>,
+    msg: &str,
+    note_span: Option<Span>,
+    note: &str,
+) {
+    cx.struct_span_lint(lint, span, |diag| {
+        let mut diag = diag.build(msg);
+        if let Some(note_span) = note_span {
+            diag.span_note(note_span, note);
+        } else {
+            diag.note(note);
+        }
+        docs_link(&mut diag, lint);
+        diag.emit();
+    });
+}
+
+/// Like `span_lint` but allows to add notes, help and suggestions using a closure.
+///
+/// If you need to customize your lint output a lot, use this function.
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
+pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
+where
+    F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
+{
+    cx.struct_span_lint(lint, sp, |diag| {
+        let mut diag = diag.build(msg);
+        f(&mut diag);
+        docs_link(&mut diag, lint);
+        diag.emit();
+    });
+}
+
+pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) {
+    cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
+        let mut diag = diag.build(msg);
+        docs_link(&mut diag, lint);
+        diag.emit();
+    });
+}
+
+pub fn span_lint_hir_and_then(
+    cx: &LateContext<'_>,
+    lint: &'static Lint,
+    hir_id: HirId,
+    sp: Span,
+    msg: &str,
+    f: impl FnOnce(&mut DiagnosticBuilder<'_>),
+) {
+    cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
+        let mut diag = diag.build(msg);
+        f(&mut diag);
+        docs_link(&mut diag, lint);
+        diag.emit();
+    });
+}
+
+/// Add a span lint with a suggestion on how to fix it.
+///
+/// These suggestions can be parsed by rustfix to allow it to automatically fix your code.
+/// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x >
+/// 2)"`.
+///
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
+///
+/// # Example
+///
+/// ```ignore
+/// error: This `.fold` can be more succinctly expressed as `.any`
+/// --> $DIR/methods.rs:390:13
+///     |
+/// 390 |     let _ = (0..3).fold(false, |acc, x| acc || x > 2);
+///     |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
+///     |
+///     = note: `-D fold-any` implied by `-D warnings`
+/// ```
+#[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))]
+pub fn span_lint_and_sugg<'a, T: LintContext>(
+    cx: &'a T,
+    lint: &'static Lint,
+    sp: Span,
+    msg: &str,
+    help: &str,
+    sugg: String,
+    applicability: Applicability,
+) {
+    span_lint_and_then(cx, lint, sp, msg, |diag| {
+        diag.span_suggestion(sp, help, sugg, applicability);
+    });
+}
+
+/// Create a suggestion made from several `span → replacement`.
+///
+/// Note: in the JSON format (used by `compiletest_rs`), the help message will
+/// appear once per
+/// replacement. In human-readable format though, it only appears once before
+/// the whole suggestion.
+pub fn multispan_sugg<I>(diag: &mut DiagnosticBuilder<'_>, help_msg: &str, sugg: I)
+where
+    I: IntoIterator<Item = (Span, String)>,
+{
+    multispan_sugg_with_applicability(diag, help_msg, Applicability::Unspecified, sugg)
+}
+
+pub fn multispan_sugg_with_applicability<I>(
+    diag: &mut DiagnosticBuilder<'_>,
+    help_msg: &str,
+    applicability: Applicability,
+    sugg: I,
+) where
+    I: IntoIterator<Item = (Span, String)>,
+{
+    diag.multipart_suggestion(help_msg, sugg.into_iter().collect(), applicability);
+}
diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
new file mode 100644 (file)
index 0000000..52a33e9
--- /dev/null
@@ -0,0 +1,134 @@
+//! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa.
+//!
+//! Things to consider:
+//!  - has the expression side-effects?
+//!  - is the expression computationally expensive?
+//!
+//! See lints:
+//!  - unnecessary-lazy-evaluations
+//!  - or-fun-call
+//!  - option-if-let-else
+
+use crate::{is_ctor_or_promotable_const_function, is_type_diagnostic_item, match_type, paths};
+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_lint::LateContext;
+use rustc_middle::hir::map::Map;
+
+/// 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(|expr| identify_some_pure_patterns(expr)),
+        ExprKind::Struct(_, fields, expr) => {
+            fields.iter().all(|f| identify_some_pure_patterns(f.expr))
+                && expr.map_or(true, |e| identify_some_pure_patterns(e))
+        },
+        ExprKind::Call(
+            &Expr {
+                kind:
+                    ExprKind::Path(QPath::Resolved(
+                        _,
+                        Path {
+                            res: Res::Def(DefKind::Ctor(..) | DefKind::Variant, ..),
+                            ..
+                        },
+                    )),
+                ..
+            },
+            args,
+        ) => args.iter().all(|expr| identify_some_pure_patterns(expr)),
+        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::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,
+    }
+}
+
+/// 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<'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_type))
+                        || match_type(self.cx, ty, &paths::BTREEMAP)
+                },
+                ExprKind::MethodCall(..) => true,
+                _ => false,
+            };
+
+            if call_found {
+                self.found |= true;
+            }
+
+            if !self.found {
+                intravisit::walk_expr(self, expr);
+            }
+        }
+
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::None
+        }
+    }
+
+    let mut finder = FunCallFinder { cx, found: false };
+    finder.visit_expr(expr);
+    finder.found
+}
+
+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)
+}
+
+pub fn is_lazyness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    identify_some_potentially_expensive_patterns(cx, expr)
+}
diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs
new file mode 100644 (file)
index 0000000..be22df7
--- /dev/null
@@ -0,0 +1,268 @@
+//! This module contains functions for retrieve the original AST from lowered
+//! `hir`.
+
+#![deny(clippy::missing_docs_in_private_items)]
+
+use crate::{is_expn_of, match_def_path, paths};
+use if_chain::if_chain;
+use rustc_ast::ast;
+use rustc_hir as hir;
+use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp};
+use rustc_lint::LateContext;
+use rustc_span::source_map::Span;
+
+/// Converts a hir binary operator to the corresponding `ast` type.
+#[must_use]
+pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
+    match op {
+        hir::BinOpKind::Eq => ast::BinOpKind::Eq,
+        hir::BinOpKind::Ge => ast::BinOpKind::Ge,
+        hir::BinOpKind::Gt => ast::BinOpKind::Gt,
+        hir::BinOpKind::Le => ast::BinOpKind::Le,
+        hir::BinOpKind::Lt => ast::BinOpKind::Lt,
+        hir::BinOpKind::Ne => ast::BinOpKind::Ne,
+        hir::BinOpKind::Or => ast::BinOpKind::Or,
+        hir::BinOpKind::Add => ast::BinOpKind::Add,
+        hir::BinOpKind::And => ast::BinOpKind::And,
+        hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd,
+        hir::BinOpKind::BitOr => ast::BinOpKind::BitOr,
+        hir::BinOpKind::BitXor => ast::BinOpKind::BitXor,
+        hir::BinOpKind::Div => ast::BinOpKind::Div,
+        hir::BinOpKind::Mul => ast::BinOpKind::Mul,
+        hir::BinOpKind::Rem => ast::BinOpKind::Rem,
+        hir::BinOpKind::Shl => ast::BinOpKind::Shl,
+        hir::BinOpKind::Shr => ast::BinOpKind::Shr,
+        hir::BinOpKind::Sub => ast::BinOpKind::Sub,
+    }
+}
+
+/// Represent a range akin to `ast::ExprKind::Range`.
+#[derive(Debug, Copy, Clone)]
+pub struct Range<'a> {
+    /// The lower bound of the range, or `None` for ranges such as `..X`.
+    pub start: Option<&'a hir::Expr<'a>>,
+    /// The upper bound of the range, or `None` for ranges such as `X..`.
+    pub end: Option<&'a hir::Expr<'a>>,
+    /// Whether the interval is open or closed.
+    pub limits: ast::RangeLimits,
+}
+
+/// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
+pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
+    /// Finds the field named `name` in the field. Always return `Some` for
+    /// convenience.
+    fn get_field<'c>(name: &str, fields: &'c [hir::Field<'_>]) -> Option<&'c hir::Expr<'c>> {
+        let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr;
+
+        Some(expr)
+    }
+
+    match expr.kind {
+        hir::ExprKind::Call(ref path, ref args)
+            if matches!(
+                path.kind,
+                hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _))
+            ) =>
+        {
+            Some(Range {
+                start: Some(&args[0]),
+                end: Some(&args[1]),
+                limits: ast::RangeLimits::Closed,
+            })
+        },
+        hir::ExprKind::Struct(ref path, ref fields, None) => match path {
+            hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range {
+                start: None,
+                end: None,
+                limits: ast::RangeLimits::HalfOpen,
+            }),
+            hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range {
+                start: Some(get_field("start", fields)?),
+                end: None,
+                limits: ast::RangeLimits::HalfOpen,
+            }),
+            hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range {
+                start: Some(get_field("start", fields)?),
+                end: Some(get_field("end", fields)?),
+                limits: ast::RangeLimits::HalfOpen,
+            }),
+            hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range {
+                start: None,
+                end: Some(get_field("end", fields)?),
+                limits: ast::RangeLimits::Closed,
+            }),
+            hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range {
+                start: None,
+                end: Some(get_field("end", fields)?),
+                limits: ast::RangeLimits::HalfOpen,
+            }),
+            _ => None,
+        },
+        _ => None,
+    }
+}
+
+/// Checks if a `let` statement is from a `for` loop desugaring.
+pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
+    // This will detect plain for-loops without an actual variable binding:
+    //
+    // ```
+    // for x in some_vec {
+    //     // do stuff
+    // }
+    // ```
+    if_chain! {
+        if let Some(ref expr) = local.init;
+        if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind;
+        then {
+            return true;
+        }
+    }
+
+    // This detects a variable binding in for loop to avoid `let_unit_value`
+    // lint (see issue #1964).
+    //
+    // ```
+    // for _ in vec![()] {
+    //     // anything
+    // }
+    // ```
+    if let hir::LocalSource::ForLoopDesugar = local.source {
+        return true;
+    }
+
+    false
+}
+
+/// Recover the essential nodes of a desugared for loop as well as the entire span:
+/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
+pub fn for_loop<'tcx>(
+    expr: &'tcx hir::Expr<'tcx>,
+) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>, Span)> {
+    if_chain! {
+        if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
+        if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind;
+        if iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none();
+        if let hir::ExprKind::Loop(ref block, ..) = arms[0].body.kind;
+        if block.expr.is_none();
+        if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
+        if let hir::StmtKind::Local(ref local) = let_stmt.kind;
+        if let hir::StmtKind::Expr(ref expr) = body.kind;
+        then {
+            return Some((&*local.pat, &iterargs[0], expr, arms[0].span));
+        }
+    }
+    None
+}
+
+/// Recover the essential nodes of a desugared while loop:
+/// `while cond { body }` becomes `(cond, body)`.
+pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> {
+    if_chain! {
+        if let hir::ExprKind::Loop(hir::Block { expr: Some(expr), .. }, _, hir::LoopSource::While, _) = &expr.kind;
+        if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind;
+        if let hir::ExprKind::DropTemps(cond) = &cond.kind;
+        if let [hir::Arm { body, .. }, ..] = &arms[..];
+        then {
+            return Some((cond, body));
+        }
+    }
+    None
+}
+
+/// Represent the pre-expansion arguments of a `vec!` invocation.
+pub enum VecArgs<'a> {
+    /// `vec![elem; len]`
+    Repeat(&'a hir::Expr<'a>, &'a hir::Expr<'a>),
+    /// `vec![a, b, c]`
+    Vec(&'a [hir::Expr<'a>]),
+}
+
+/// Returns the arguments of the `vec!` macro if this expression was expanded
+/// from `vec!`.
+pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option<VecArgs<'e>> {
+    if_chain! {
+        if let hir::ExprKind::Call(ref fun, ref args) = expr.kind;
+        if let hir::ExprKind::Path(ref qpath) = fun.kind;
+        if is_expn_of(fun.span, "vec").is_some();
+        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
+        then {
+            return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
+                // `vec![elem; size]` case
+                Some(VecArgs::Repeat(&args[0], &args[1]))
+            }
+            else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
+                // `vec![a, b, c]` case
+                if_chain! {
+                    if let hir::ExprKind::Box(ref boxed) = args[0].kind;
+                    if let hir::ExprKind::Array(ref args) = boxed.kind;
+                    then {
+                        return Some(VecArgs::Vec(&*args));
+                    }
+                }
+
+                None
+            }
+            else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() {
+                Some(VecArgs::Vec(&[]))
+            }
+            else {
+                None
+            };
+        }
+    }
+
+    None
+}
+
+/// Extract args from an assert-like macro.
+/// Currently working with:
+/// - `assert!`, `assert_eq!` and `assert_ne!`
+/// - `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!`
+/// For example:
+/// `assert!(expr)` will return Some([expr])
+/// `debug_assert_eq!(a, b)` will return Some([a, b])
+pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx Expr<'tcx>>> {
+    /// Try to match the AST for a pattern that contains a match, for example when two args are
+    /// compared
+    fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option<Vec<&Expr<'_>>> {
+        if_chain! {
+            if let ExprKind::Match(ref headerexpr, _, _) = &matchblock_expr.kind;
+            if let ExprKind::Tup([lhs, rhs]) = &headerexpr.kind;
+            if let ExprKind::AddrOf(BorrowKind::Ref, _, lhs) = lhs.kind;
+            if let ExprKind::AddrOf(BorrowKind::Ref, _, rhs) = rhs.kind;
+            then {
+                return Some(vec![lhs, rhs]);
+            }
+        }
+        None
+    }
+
+    if let ExprKind::Block(ref block, _) = e.kind {
+        if block.stmts.len() == 1 {
+            if let StmtKind::Semi(ref matchexpr) = block.stmts.get(0)?.kind {
+                // macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`)
+                if_chain! {
+                    if let ExprKind::If(ref clause, _, _)  = matchexpr.kind;
+                    if let ExprKind::Unary(UnOp::Not, condition) = clause.kind;
+                    then {
+                        return Some(vec![condition]);
+                    }
+                }
+
+                // debug macros with two args: `debug_assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
+                if_chain! {
+                    if let ExprKind::Block(ref matchblock,_) = matchexpr.kind;
+                    if let Some(ref matchblock_expr) = matchblock.expr;
+                    then {
+                        return ast_matchblock(matchblock_expr);
+                    }
+                }
+            }
+        } else if let Some(matchblock_expr) = block.expr {
+            // macros with two args: `assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
+            return ast_matchblock(&matchblock_expr);
+        }
+    }
+    None
+}
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
new file mode 100644 (file)
index 0000000..81be925
--- /dev/null
@@ -0,0 +1,852 @@
+use crate::consts::{constant_context, constant_simple};
+use crate::differing_macro_contexts;
+use rustc_ast::ast::InlineAsmTemplatePiece;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_hir::def::Res;
+use rustc_hir::{
+    BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat, FnRetTy,
+    GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path,
+    PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
+};
+use rustc_lint::LateContext;
+use rustc_middle::ich::StableHashingContextProvider;
+use rustc_middle::ty::TypeckResults;
+use rustc_span::Symbol;
+use std::hash::Hash;
+
+/// Type used to check whether two ast are the same. This is different from the
+/// operator
+/// `==` on ast types as this operator would compare true equality with ID and
+/// span.
+///
+/// Note that some expressions kinds are not considered but could be added.
+pub struct SpanlessEq<'a, 'tcx> {
+    /// Context used to evaluate constant expressions.
+    cx: &'a LateContext<'tcx>,
+    maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
+    allow_side_effects: bool,
+    expr_fallback: Option<Box<dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a>>,
+}
+
+impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
+    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
+        Self {
+            cx,
+            maybe_typeck_results: cx.maybe_typeck_results(),
+            allow_side_effects: true,
+            expr_fallback: None,
+        }
+    }
+
+    /// Consider expressions containing potential side effects as not equal.
+    pub fn deny_side_effects(self) -> Self {
+        Self {
+            allow_side_effects: false,
+            ..self
+        }
+    }
+
+    pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
+        Self {
+            expr_fallback: Some(Box::new(expr_fallback)),
+            ..self
+        }
+    }
+
+    /// Use this method to wrap comparisons that may involve inter-expression context.
+    /// See `self.locals`.
+    fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> {
+        HirEqInterExpr {
+            inner: self,
+            locals: FxHashMap::default(),
+        }
+    }
+
+    pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
+        self.inter_expr().eq_block(left, right)
+    }
+
+    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
+        self.inter_expr().eq_expr(left, right)
+    }
+
+    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
+        self.inter_expr().eq_path_segment(left, right)
+    }
+
+    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
+        self.inter_expr().eq_path_segments(left, right)
+    }
+
+    pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool {
+        self.inter_expr().eq_ty_kind(left, right)
+    }
+}
+
+struct HirEqInterExpr<'a, 'b, 'tcx> {
+    inner: &'a mut SpanlessEq<'b, 'tcx>,
+
+    // When binding are declared, the binding ID in the left expression is mapped to the one on the
+    // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`,
+    // these blocks are considered equal since `x` is mapped to `y`.
+    locals: FxHashMap<HirId, HirId>,
+}
+
+impl HirEqInterExpr<'_, '_, '_> {
+    fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
+        match (&left.kind, &right.kind) {
+            (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => {
+                self.eq_pat(&l.pat, &r.pat)
+                    && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r))
+                    && both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
+            },
+            (&StmtKind::Expr(ref l), &StmtKind::Expr(ref r)) | (&StmtKind::Semi(ref l), &StmtKind::Semi(ref r)) => {
+                self.eq_expr(l, r)
+            },
+            _ => false,
+        }
+    }
+
+    /// Checks whether two blocks are the same.
+    fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
+        over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r))
+            && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
+    }
+
+    #[allow(clippy::similar_names)]
+    fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
+        if !self.inner.allow_side_effects && differing_macro_contexts(left.span, right.span) {
+            return false;
+        }
+
+        if let Some(typeck_results) = self.inner.maybe_typeck_results {
+            if let (Some(l), Some(r)) = (
+                constant_simple(self.inner.cx, typeck_results, left),
+                constant_simple(self.inner.cx, typeck_results, right),
+            ) {
+                if l == r {
+                    return true;
+                }
+            }
+        }
+
+        let is_eq = match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) {
+            (&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => {
+                lb == rb && l_mut == r_mut && self.eq_expr(le, re)
+            },
+            (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => {
+                both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
+            },
+            (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => {
+                self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
+            },
+            (&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => {
+                self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
+            },
+            (&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r),
+            (&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref rr)) => {
+                l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
+                    || swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| {
+                        l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
+                    })
+            },
+            (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => {
+                both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
+                    && both(le, re, |l, r| self.eq_expr(l, r))
+            },
+            (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r),
+            (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
+                self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
+            },
+            (&ExprKind::Cast(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt))
+            | (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => {
+                self.eq_expr(lx, rx) && self.eq_ty(lt, rt)
+            },
+            (&ExprKind::Field(ref l_f_exp, ref l_f_ident), &ExprKind::Field(ref r_f_exp, ref r_f_ident)) => {
+                l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp)
+            },
+            (&ExprKind::Index(ref la, ref li), &ExprKind::Index(ref ra, ref ri)) => {
+                self.eq_expr(la, ra) && self.eq_expr(li, ri)
+            },
+            (&ExprKind::If(ref lc, ref lt, ref le), &ExprKind::If(ref rc, ref rt, ref re)) => {
+                self.eq_expr(lc, rc) && self.eq_expr(&**lt, &**rt) && both(le, re, |l, r| self.eq_expr(l, r))
+            },
+            (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
+            (&ExprKind::Loop(ref lb, ref ll, ref lls, _), &ExprKind::Loop(ref rb, ref rl, ref rls, _)) => {
+                lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
+            },
+            (&ExprKind::Match(ref le, ref la, ref ls), &ExprKind::Match(ref re, ref ra, ref rs)) => {
+                ls == rs
+                    && self.eq_expr(le, re)
+                    && over(la, ra, |l, r| {
+                        self.eq_pat(&l.pat, &r.pat)
+                            && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r))
+                            && self.eq_expr(&l.body, &r.body)
+                    })
+            },
+            (&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => {
+                self.inner.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args)
+            },
+            (&ExprKind::Repeat(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => {
+                let mut celcx = constant_context(self.inner.cx, self.inner.cx.tcx.typeck_body(ll_id.body));
+                let ll = celcx.expr(&self.inner.cx.tcx.hir().body(ll_id.body).value);
+                let mut celcx = constant_context(self.inner.cx, self.inner.cx.tcx.typeck_body(rl_id.body));
+                let rl = celcx.expr(&self.inner.cx.tcx.hir().body(rl_id.body).value);
+
+                self.eq_expr(le, re) && ll == rl
+            },
+            (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)),
+            (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r),
+            (&ExprKind::Struct(ref l_path, ref lf, ref lo), &ExprKind::Struct(ref r_path, ref rf, ref ro)) => {
+                self.eq_qpath(l_path, r_path)
+                    && both(lo, ro, |l, r| self.eq_expr(l, r))
+                    && over(lf, rf, |l, r| self.eq_field(l, r))
+            },
+            (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
+            (&ExprKind::Unary(l_op, ref le), &ExprKind::Unary(r_op, ref re)) => l_op == r_op && self.eq_expr(le, re),
+            (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
+            (&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re),
+            _ => false,
+        };
+        is_eq || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right))
+    }
+
+    fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
+        over(left, right, |l, r| self.eq_expr(l, r))
+    }
+
+    fn eq_field(&mut self, left: &Field<'_>, right: &Field<'_>) -> bool {
+        left.ident.name == right.ident.name && self.eq_expr(&left.expr, &right.expr)
+    }
+
+    fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
+        match (left, right) {
+            (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
+            (Guard::IfLet(lp, le), Guard::IfLet(rp, re)) => self.eq_pat(lp, rp) && self.eq_expr(le, re),
+            _ => false,
+        }
+    }
+
+    fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
+        match (left, right) {
+            (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt),
+            (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty, r_ty),
+            _ => false,
+        }
+    }
+
+    fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
+        left.name == right.name
+    }
+
+    fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool {
+        let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right);
+        li.name == ri.name && self.eq_pat(lp, rp)
+    }
+
+    /// Checks whether two patterns are the same.
+    fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
+        match (&left.kind, &right.kind) {
+            (&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r),
+            (&PatKind::Struct(ref lp, ref la, ..), &PatKind::Struct(ref rp, ref ra, ..)) => {
+                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_fieldpat(l, r))
+            },
+            (&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => {
+                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
+            },
+            (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => {
+                let eq = lb == rb && both(lp, rp, |l, r| self.eq_pat(l, r));
+                if eq {
+                    self.locals.insert(li, ri);
+                }
+                eq
+            },
+            (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
+            (&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r),
+            (&PatKind::Tuple(ref l, ls), &PatKind::Tuple(ref r, rs)) => {
+                ls == rs && over(l, r, |l, r| self.eq_pat(l, r))
+            },
+            (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
+                both(ls, rs, |a, b| self.eq_expr(a, b)) && both(le, re, |a, b| self.eq_expr(a, b)) && (li == ri)
+            },
+            (&PatKind::Ref(ref le, ref lm), &PatKind::Ref(ref re, ref rm)) => lm == rm && self.eq_pat(le, re),
+            (&PatKind::Slice(ref ls, ref li, ref le), &PatKind::Slice(ref rs, ref ri, ref re)) => {
+                over(ls, rs, |l, r| self.eq_pat(l, r))
+                    && over(le, re, |l, r| self.eq_pat(l, r))
+                    && both(li, ri, |l, r| self.eq_pat(l, r))
+            },
+            (&PatKind::Wild, &PatKind::Wild) => true,
+            _ => false,
+        }
+    }
+
+    #[allow(clippy::similar_names)]
+    fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
+        match (left, right) {
+            (&QPath::Resolved(ref lty, ref lpath), &QPath::Resolved(ref rty, ref rpath)) => {
+                both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
+            },
+            (&QPath::TypeRelative(ref lty, ref lseg), &QPath::TypeRelative(ref rty, ref rseg)) => {
+                self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
+            },
+            (&QPath::LangItem(llang_item, _), &QPath::LangItem(rlang_item, _)) => llang_item == rlang_item,
+            _ => false,
+        }
+    }
+
+    fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
+        match (left.res, right.res) {
+            (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r),
+            (Res::Local(_), _) | (_, Res::Local(_)) => false,
+            _ => over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r)),
+        }
+    }
+
+    fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
+        if !(left.parenthesized || right.parenthesized) {
+            over(&left.args, &right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work
+                && over(&left.bindings, &right.bindings, |l, r| self.eq_type_binding(l, r))
+        } else if left.parenthesized && right.parenthesized {
+            over(left.inputs(), right.inputs(), |l, r| self.eq_ty(l, r))
+                && both(&Some(&left.bindings[0].ty()), &Some(&right.bindings[0].ty()), |l, r| {
+                    self.eq_ty(l, r)
+                })
+        } else {
+            false
+        }
+    }
+
+    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
+        left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r))
+    }
+
+    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
+        // The == of idents doesn't work with different contexts,
+        // we have to be explicit about hygiene
+        left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
+    }
+
+    fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
+        self.eq_ty_kind(&left.kind, &right.kind)
+    }
+
+    #[allow(clippy::similar_names)]
+    fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool {
+        match (left, right) {
+            (&TyKind::Slice(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec),
+            (&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => {
+                let cx = self.inner.cx;
+                let eval_const =
+                    |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value);
+                self.eq_ty(lt, rt) && eval_const(ll_id.body) == eval_const(rl_id.body)
+            },
+            (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
+                l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty)
+            },
+            (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => {
+                l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(&*l_rmut.ty, &*r_rmut.ty)
+            },
+            (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r),
+            (&TyKind::Tup(ref l), &TyKind::Tup(ref r)) => over(l, r, |l, r| self.eq_ty(l, r)),
+            (&TyKind::Infer, &TyKind::Infer) => true,
+            _ => false,
+        }
+    }
+
+    fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool {
+        left.ident.name == right.ident.name && self.eq_ty(&left.ty(), &right.ty())
+    }
+}
+
+/// Some simple reductions like `{ return }` => `return`
+fn reduce_exprkind<'hir>(kind: &'hir ExprKind<'hir>) -> &ExprKind<'hir> {
+    if let ExprKind::Block(block, _) = kind {
+        match (block.stmts, block.expr) {
+            // `{}` => `()`
+            ([], None) => &ExprKind::Tup(&[]),
+            ([], Some(expr)) => match expr.kind {
+                // `{ return .. }` => `return ..`
+                ExprKind::Ret(..) => &expr.kind,
+                _ => kind,
+            },
+            ([stmt], None) => match stmt.kind {
+                StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind {
+                    // `{ return ..; }` => `return ..`
+                    ExprKind::Ret(..) => &expr.kind,
+                    _ => kind,
+                },
+                _ => kind,
+            },
+            _ => kind,
+        }
+    } else {
+        kind
+    }
+}
+
+fn swap_binop<'a>(
+    binop: BinOpKind,
+    lhs: &'a Expr<'a>,
+    rhs: &'a Expr<'a>,
+) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
+    match binop {
+        BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => {
+            Some((binop, rhs, lhs))
+        },
+        BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
+        BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
+        BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
+        BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
+        BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698
+        | BinOpKind::Shl
+        | BinOpKind::Shr
+        | BinOpKind::Rem
+        | BinOpKind::Sub
+        | BinOpKind::Div
+        | BinOpKind::And
+        | BinOpKind::Or => None,
+    }
+}
+
+/// Checks if the two `Option`s are both `None` or some equal values as per
+/// `eq_fn`.
+pub fn both<X>(l: &Option<X>, r: &Option<X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
+    l.as_ref()
+        .map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
+}
+
+/// Checks if two slices are equal as per `eq_fn`.
+pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
+    left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
+}
+
+/// Checks if two expressions evaluate to the same value, and don't contain any side effects.
+pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
+    SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
+}
+
+/// Type used to hash an ast element. This is different from the `Hash` trait
+/// on ast types as this
+/// trait would consider IDs and spans.
+///
+/// All expressions kind are hashed, but some might have a weaker hash.
+pub struct SpanlessHash<'a, 'tcx> {
+    /// Context used to evaluate constant expressions.
+    cx: &'a LateContext<'tcx>,
+    maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
+    s: StableHasher,
+}
+
+impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
+    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
+        Self {
+            cx,
+            maybe_typeck_results: cx.maybe_typeck_results(),
+            s: StableHasher::new(),
+        }
+    }
+
+    pub fn finish(self) -> u64 {
+        self.s.finish()
+    }
+
+    pub fn hash_block(&mut self, b: &Block<'_>) {
+        for s in b.stmts {
+            self.hash_stmt(s);
+        }
+
+        if let Some(ref e) = b.expr {
+            self.hash_expr(e);
+        }
+
+        match b.rules {
+            BlockCheckMode::DefaultBlock => 0,
+            BlockCheckMode::UnsafeBlock(_) => 1,
+            BlockCheckMode::PushUnsafeBlock(_) => 2,
+            BlockCheckMode::PopUnsafeBlock(_) => 3,
+        }
+        .hash(&mut self.s);
+    }
+
+    #[allow(clippy::many_single_char_names, clippy::too_many_lines)]
+    pub fn hash_expr(&mut self, e: &Expr<'_>) {
+        let simple_const = self
+            .maybe_typeck_results
+            .and_then(|typeck_results| constant_simple(self.cx, typeck_results, e));
+
+        // const hashing may result in the same hash as some unrelated node, so add a sort of
+        // discriminant depending on which path we're choosing next
+        simple_const.is_some().hash(&mut self.s);
+
+        if let Some(e) = simple_const {
+            return e.hash(&mut self.s);
+        }
+
+        std::mem::discriminant(&e.kind).hash(&mut self.s);
+
+        match e.kind {
+            ExprKind::AddrOf(kind, m, ref e) => {
+                match kind {
+                    BorrowKind::Ref => 0,
+                    BorrowKind::Raw => 1,
+                }
+                .hash(&mut self.s);
+                m.hash(&mut self.s);
+                self.hash_expr(e);
+            },
+            ExprKind::Continue(i) => {
+                if let Some(i) = i.label {
+                    self.hash_name(i.ident.name);
+                }
+            },
+            ExprKind::Assign(ref l, ref r, _) => {
+                self.hash_expr(l);
+                self.hash_expr(r);
+            },
+            ExprKind::AssignOp(ref o, ref l, ref r) => {
+                o.node
+                    .hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
+                self.hash_expr(l);
+                self.hash_expr(r);
+            },
+            ExprKind::Block(ref b, _) => {
+                self.hash_block(b);
+            },
+            ExprKind::Binary(op, ref l, ref r) => {
+                op.node
+                    .hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
+                self.hash_expr(l);
+                self.hash_expr(r);
+            },
+            ExprKind::Break(i, ref j) => {
+                if let Some(i) = i.label {
+                    self.hash_name(i.ident.name);
+                }
+                if let Some(ref j) = *j {
+                    self.hash_expr(&*j);
+                }
+            },
+            ExprKind::Box(ref e) | ExprKind::DropTemps(ref e) | ExprKind::Yield(ref e, _) => {
+                self.hash_expr(e);
+            },
+            ExprKind::Call(ref fun, args) => {
+                self.hash_expr(fun);
+                self.hash_exprs(args);
+            },
+            ExprKind::Cast(ref e, ref ty) | ExprKind::Type(ref e, ref ty) => {
+                self.hash_expr(e);
+                self.hash_ty(ty);
+            },
+            ExprKind::Closure(cap, _, eid, _, _) => {
+                match cap {
+                    CaptureBy::Value => 0,
+                    CaptureBy::Ref => 1,
+                }
+                .hash(&mut self.s);
+                // closures inherit TypeckResults
+                self.hash_expr(&self.cx.tcx.hir().body(eid).value);
+            },
+            ExprKind::Field(ref e, ref f) => {
+                self.hash_expr(e);
+                self.hash_name(f.name);
+            },
+            ExprKind::Index(ref a, ref i) => {
+                self.hash_expr(a);
+                self.hash_expr(i);
+            },
+            ExprKind::InlineAsm(ref asm) => {
+                for piece in asm.template {
+                    match piece {
+                        InlineAsmTemplatePiece::String(s) => s.hash(&mut self.s),
+                        InlineAsmTemplatePiece::Placeholder {
+                            operand_idx,
+                            modifier,
+                            span: _,
+                        } => {
+                            operand_idx.hash(&mut self.s);
+                            modifier.hash(&mut self.s);
+                        },
+                    }
+                }
+                asm.options.hash(&mut self.s);
+                for (op, _op_sp) in asm.operands {
+                    match op {
+                        InlineAsmOperand::In { reg, expr } => {
+                            reg.hash(&mut self.s);
+                            self.hash_expr(expr);
+                        },
+                        InlineAsmOperand::Out { reg, late, expr } => {
+                            reg.hash(&mut self.s);
+                            late.hash(&mut self.s);
+                            if let Some(expr) = expr {
+                                self.hash_expr(expr);
+                            }
+                        },
+                        InlineAsmOperand::InOut { reg, late, expr } => {
+                            reg.hash(&mut self.s);
+                            late.hash(&mut self.s);
+                            self.hash_expr(expr);
+                        },
+                        InlineAsmOperand::SplitInOut {
+                            reg,
+                            late,
+                            in_expr,
+                            out_expr,
+                        } => {
+                            reg.hash(&mut self.s);
+                            late.hash(&mut self.s);
+                            self.hash_expr(in_expr);
+                            if let Some(out_expr) = out_expr {
+                                self.hash_expr(out_expr);
+                            }
+                        },
+                        InlineAsmOperand::Const { expr } | InlineAsmOperand::Sym { expr } => self.hash_expr(expr),
+                    }
+                }
+            },
+            ExprKind::LlvmInlineAsm(..) | ExprKind::Err => {},
+            ExprKind::Lit(ref l) => {
+                l.node.hash(&mut self.s);
+            },
+            ExprKind::Loop(ref b, ref i, ..) => {
+                self.hash_block(b);
+                if let Some(i) = *i {
+                    self.hash_name(i.ident.name);
+                }
+            },
+            ExprKind::If(ref cond, ref then, ref else_opt) => {
+                let c: fn(_, _, _) -> _ = ExprKind::If;
+                c.hash(&mut self.s);
+                self.hash_expr(cond);
+                self.hash_expr(&**then);
+                if let Some(ref e) = *else_opt {
+                    self.hash_expr(e);
+                }
+            },
+            ExprKind::Match(ref e, arms, ref s) => {
+                self.hash_expr(e);
+
+                for arm in arms {
+                    // TODO: arm.pat?
+                    if let Some(ref e) = arm.guard {
+                        self.hash_guard(e);
+                    }
+                    self.hash_expr(&arm.body);
+                }
+
+                s.hash(&mut self.s);
+            },
+            ExprKind::MethodCall(ref path, ref _tys, args, ref _fn_span) => {
+                self.hash_name(path.ident.name);
+                self.hash_exprs(args);
+            },
+            ExprKind::ConstBlock(ref l_id) => {
+                self.hash_body(l_id.body);
+            },
+            ExprKind::Repeat(ref e, ref l_id) => {
+                self.hash_expr(e);
+                self.hash_body(l_id.body);
+            },
+            ExprKind::Ret(ref e) => {
+                if let Some(ref e) = *e {
+                    self.hash_expr(e);
+                }
+            },
+            ExprKind::Path(ref qpath) => {
+                self.hash_qpath(qpath);
+            },
+            ExprKind::Struct(ref path, fields, ref expr) => {
+                self.hash_qpath(path);
+
+                for f in fields {
+                    self.hash_name(f.ident.name);
+                    self.hash_expr(&f.expr);
+                }
+
+                if let Some(ref e) = *expr {
+                    self.hash_expr(e);
+                }
+            },
+            ExprKind::Tup(tup) => {
+                self.hash_exprs(tup);
+            },
+            ExprKind::Array(v) => {
+                self.hash_exprs(v);
+            },
+            ExprKind::Unary(lop, ref le) => {
+                lop.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
+                self.hash_expr(le);
+            },
+        }
+    }
+
+    pub fn hash_exprs(&mut self, e: &[Expr<'_>]) {
+        for e in e {
+            self.hash_expr(e);
+        }
+    }
+
+    pub fn hash_name(&mut self, n: Symbol) {
+        n.as_str().hash(&mut self.s);
+    }
+
+    pub fn hash_qpath(&mut self, p: &QPath<'_>) {
+        match *p {
+            QPath::Resolved(_, ref path) => {
+                self.hash_path(path);
+            },
+            QPath::TypeRelative(_, ref path) => {
+                self.hash_name(path.ident.name);
+            },
+            QPath::LangItem(lang_item, ..) => {
+                lang_item.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
+            },
+        }
+        // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
+    }
+
+    pub fn hash_path(&mut self, path: &Path<'_>) {
+        match path.res {
+            // constant hash since equality is dependant on inter-expression context
+            Res::Local(_) => 1_usize.hash(&mut self.s),
+            _ => {
+                for seg in path.segments {
+                    self.hash_name(seg.ident.name);
+                }
+            },
+        }
+    }
+
+    pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
+        std::mem::discriminant(&b.kind).hash(&mut self.s);
+
+        match &b.kind {
+            StmtKind::Local(local) => {
+                if let Some(ref init) = local.init {
+                    self.hash_expr(init);
+                }
+            },
+            StmtKind::Item(..) => {},
+            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
+                self.hash_expr(expr);
+            },
+        }
+    }
+
+    pub fn hash_guard(&mut self, g: &Guard<'_>) {
+        match g {
+            Guard::If(ref expr) | Guard::IfLet(_, ref expr) => {
+                self.hash_expr(expr);
+            },
+        }
+    }
+
+    pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
+        std::mem::discriminant(&lifetime.name).hash(&mut self.s);
+        if let LifetimeName::Param(ref name) = lifetime.name {
+            std::mem::discriminant(name).hash(&mut self.s);
+            match name {
+                ParamName::Plain(ref ident) => {
+                    ident.name.hash(&mut self.s);
+                },
+                ParamName::Fresh(ref size) => {
+                    size.hash(&mut self.s);
+                },
+                ParamName::Error => {},
+            }
+        }
+    }
+
+    pub fn hash_ty(&mut self, ty: &Ty<'_>) {
+        self.hash_tykind(&ty.kind);
+    }
+
+    pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
+        std::mem::discriminant(ty).hash(&mut self.s);
+        match ty {
+            TyKind::Slice(ty) => {
+                self.hash_ty(ty);
+            },
+            TyKind::Array(ty, anon_const) => {
+                self.hash_ty(ty);
+                self.hash_body(anon_const.body);
+            },
+            TyKind::Ptr(mut_ty) => {
+                self.hash_ty(&mut_ty.ty);
+                mut_ty.mutbl.hash(&mut self.s);
+            },
+            TyKind::Rptr(lifetime, mut_ty) => {
+                self.hash_lifetime(lifetime);
+                self.hash_ty(&mut_ty.ty);
+                mut_ty.mutbl.hash(&mut self.s);
+            },
+            TyKind::BareFn(bfn) => {
+                bfn.unsafety.hash(&mut self.s);
+                bfn.abi.hash(&mut self.s);
+                for arg in bfn.decl.inputs {
+                    self.hash_ty(&arg);
+                }
+                match bfn.decl.output {
+                    FnRetTy::DefaultReturn(_) => {
+                        ().hash(&mut self.s);
+                    },
+                    FnRetTy::Return(ref ty) => {
+                        self.hash_ty(ty);
+                    },
+                }
+                bfn.decl.c_variadic.hash(&mut self.s);
+            },
+            TyKind::Tup(ty_list) => {
+                for ty in *ty_list {
+                    self.hash_ty(ty);
+                }
+            },
+            TyKind::Path(qpath) => match qpath {
+                QPath::Resolved(ref maybe_ty, ref path) => {
+                    if let Some(ref ty) = maybe_ty {
+                        self.hash_ty(ty);
+                    }
+                    for segment in path.segments {
+                        segment.ident.name.hash(&mut self.s);
+                        self.hash_generic_args(segment.args().args);
+                    }
+                },
+                QPath::TypeRelative(ref ty, ref segment) => {
+                    self.hash_ty(ty);
+                    segment.ident.name.hash(&mut self.s);
+                },
+                QPath::LangItem(lang_item, ..) => {
+                    lang_item.hash(&mut self.s);
+                },
+            },
+            TyKind::OpaqueDef(_, arg_list) => {
+                self.hash_generic_args(arg_list);
+            },
+            TyKind::TraitObject(_, lifetime) => {
+                self.hash_lifetime(lifetime);
+            },
+            TyKind::Typeof(anon_const) => {
+                self.hash_body(anon_const.body);
+            },
+            TyKind::Err | TyKind::Infer | TyKind::Never => {},
+        }
+    }
+
+    pub fn hash_body(&mut self, body_id: BodyId) {
+        // swap out TypeckResults when hashing a body
+        let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body_id));
+        self.hash_expr(&self.cx.tcx.hir().body(body_id).value);
+        self.maybe_typeck_results = old_maybe_typeck_results;
+    }
+
+    fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
+        for arg in arg_list {
+            match arg {
+                GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
+                GenericArg::Type(ref ty) => self.hash_ty(&ty),
+                GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
new file mode 100644 (file)
index 0000000..94b7339
--- /dev/null
@@ -0,0 +1,1883 @@
+#![feature(box_patterns)]
+#![feature(in_band_lifetimes)]
+#![feature(or_patterns)]
+#![feature(rustc_private)]
+#![recursion_limit = "512"]
+#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
+
+// FIXME: switch to something more ergonomic here, once available.
+// (Currently there is no way to opt into sysroot crates without `extern crate`.)
+extern crate rustc_ast;
+extern crate rustc_ast_pretty;
+extern crate rustc_data_structures;
+extern crate rustc_errors;
+extern crate rustc_hir;
+extern crate rustc_infer;
+extern crate rustc_lint;
+extern crate rustc_middle;
+extern crate rustc_mir;
+extern crate rustc_session;
+extern crate rustc_span;
+extern crate rustc_target;
+extern crate rustc_trait_selection;
+extern crate rustc_typeck;
+
+#[macro_use]
+pub mod sym_helper;
+
+#[allow(clippy::module_name_repetitions)]
+pub mod ast_utils;
+pub mod attrs;
+pub mod camel_case;
+pub mod comparisons;
+pub mod consts;
+mod diagnostics;
+pub mod eager_or_lazy;
+pub mod higher;
+mod hir_utils;
+pub mod numeric_literal;
+pub mod paths;
+pub mod ptr;
+pub mod qualify_min_const_fn;
+pub mod sugg;
+pub mod usage;
+pub mod visitors;
+
+pub use self::attrs::*;
+pub use self::diagnostics::*;
+pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
+
+use std::borrow::Cow;
+use std::collections::hash_map::Entry;
+use std::hash::BuildHasherDefault;
+
+use if_chain::if_chain;
+use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind, Mutability};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
+use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::Node;
+use rustc_hir::{
+    def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind,
+    MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef, TyKind, Unsafety,
+};
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_lint::{LateContext, Level, Lint, LintContext};
+use rustc_middle::hir::exports::Export;
+use rustc_middle::hir::map::Map;
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
+use rustc_middle::ty::{self, layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable};
+use rustc_semver::RustcVersion;
+use rustc_session::Session;
+use rustc_span::hygiene::{ExpnKind, MacroKind};
+use rustc_span::source_map::original_sp;
+use rustc_span::sym;
+use rustc_span::symbol::{kw, Symbol};
+use rustc_span::{BytePos, Pos, Span, DUMMY_SP};
+use rustc_target::abi::Integer;
+use rustc_trait_selection::traits::query::normalize::AtExt;
+use smallvec::SmallVec;
+
+use crate::consts::{constant, Constant};
+
+pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
+    if let Ok(version) = RustcVersion::parse(msrv) {
+        return Some(version);
+    } else if let Some(sess) = sess {
+        if let Some(span) = span {
+            sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv));
+        }
+    }
+    None
+}
+
+pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool {
+    msrv.map_or(true, |msrv| msrv.meets(*lint_msrv))
+}
+
+#[macro_export]
+macro_rules! extract_msrv_attr {
+    (LateContext) => {
+        extract_msrv_attr!(@LateContext, ());
+    };
+    (EarlyContext) => {
+        extract_msrv_attr!(@EarlyContext);
+    };
+    (@$context:ident$(, $call:tt)?) => {
+        fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'tcx>, attrs: &'tcx [rustc_ast::ast::Attribute]) {
+            use $crate::get_unique_inner_attr;
+            match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") {
+                Some(msrv_attr) => {
+                    if let Some(msrv) = msrv_attr.value_str() {
+                        self.msrv = $crate::parse_msrv(
+                            &msrv.to_string(),
+                            Some(cx.sess$($call)?),
+                            Some(msrv_attr.span),
+                        );
+                    } else {
+                        cx.sess$($call)?.span_err(msrv_attr.span, "bad clippy attribute");
+                    }
+                },
+                _ => (),
+            }
+        }
+    };
+}
+
+/// Returns `true` if the two spans come from differing expansions (i.e., one is
+/// from a macro and one isn't).
+#[must_use]
+pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
+    rhs.ctxt() != lhs.ctxt()
+}
+
+/// Returns `true` if the given `NodeId` is inside a constant context
+///
+/// # Example
+///
+/// ```rust,ignore
+/// if in_constant(cx, expr.hir_id) {
+///     // Do something
+/// }
+/// ```
+pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
+    let parent_id = cx.tcx.hir().get_parent_item(id);
+    match cx.tcx.hir().get(parent_id) {
+        Node::Item(&Item {
+            kind: ItemKind::Const(..) | ItemKind::Static(..),
+            ..
+        })
+        | Node::TraitItem(&TraitItem {
+            kind: TraitItemKind::Const(..),
+            ..
+        })
+        | Node::ImplItem(&ImplItem {
+            kind: ImplItemKind::Const(..),
+            ..
+        })
+        | Node::AnonConst(_) => true,
+        Node::Item(&Item {
+            kind: ItemKind::Fn(ref sig, ..),
+            ..
+        })
+        | Node::ImplItem(&ImplItem {
+            kind: ImplItemKind::Fn(ref sig, _),
+            ..
+        }) => sig.header.constness == Constness::Const,
+        _ => false,
+    }
+}
+
+/// Returns `true` if this `span` was expanded by any macro.
+#[must_use]
+pub fn in_macro(span: Span) -> bool {
+    if span.from_expansion() {
+        !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
+    } else {
+        false
+    }
+}
+
+// If the snippet is empty, it's an attribute that was inserted during macro
+// expansion and we want to ignore those, because they could come from external
+// sources that the user has no control over.
+// For some reason these attributes don't have any expansion info on them, so
+// we have to check it this way until there is a better way.
+pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
+    if let Some(snippet) = snippet_opt(cx, span) {
+        if snippet.is_empty() {
+            return false;
+        }
+    }
+    true
+}
+
+/// Checks if given pattern is a wildcard (`_`)
+pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool {
+    matches!(pat.kind, PatKind::Wild)
+}
+
+/// Checks if type is struct, enum or union type with the given def path.
+///
+/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
+/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
+pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
+    match ty.kind() {
+        ty::Adt(adt, _) => match_def_path(cx, adt.did, path),
+        _ => false,
+    }
+}
+
+/// Checks if the type is equal to a diagnostic item
+///
+/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
+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),
+        _ => false,
+    }
+}
+
+/// Checks if the type is equal to a lang item
+pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
+    match ty.kind() {
+        ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).unwrap() == adt.did,
+        _ => false,
+    }
+}
+
+/// Checks if the method call given in `expr` belongs to the given trait.
+pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
+    let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
+    let trt_id = cx.tcx.trait_of_item(def_id);
+    trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
+}
+
+/// Checks if an expression references a variable of the given name.
+pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool {
+    if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
+        if let [p] = path.segments {
+            return p.ident.name == var;
+        }
+    }
+    false
+}
+
+pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
+    match *path {
+        QPath::Resolved(_, ref path) => path.segments.last().expect("A path must have at least one segment"),
+        QPath::TypeRelative(_, ref seg) => seg,
+        QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
+    }
+}
+
+pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
+    match *path {
+        QPath::Resolved(_, ref path) => path.segments.get(0),
+        QPath::TypeRelative(_, ref seg) => Some(seg),
+        QPath::LangItem(..) => None,
+    }
+}
+
+/// Matches a `QPath` against a slice of segment string literals.
+///
+/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
+/// `rustc_hir::QPath`.
+///
+/// # Examples
+/// ```rust,ignore
+/// match_qpath(path, &["std", "rt", "begin_unwind"])
+/// ```
+pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
+    match *path {
+        QPath::Resolved(_, ref path) => match_path(path, segments),
+        QPath::TypeRelative(ref ty, ref segment) => match ty.kind {
+            TyKind::Path(ref inner_path) => {
+                if let [prefix @ .., end] = segments {
+                    if match_qpath(inner_path, prefix) {
+                        return segment.ident.name.as_str() == *end;
+                    }
+                }
+                false
+            },
+            _ => false,
+        },
+        QPath::LangItem(..) => false,
+    }
+}
+
+/// Matches a `Path` against a slice of segment string literals.
+///
+/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
+/// `rustc_hir::Path`.
+///
+/// # Examples
+///
+/// ```rust,ignore
+/// if match_path(&trait_ref.path, &paths::HASH) {
+///     // This is the `std::hash::Hash` trait.
+/// }
+///
+/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
+///     // This is a `rustc_middle::lint::Lint`.
+/// }
+/// ```
+pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
+    path.segments
+        .iter()
+        .rev()
+        .zip(segments.iter().rev())
+        .all(|(a, b)| a.ident.name.as_str() == *b)
+}
+
+/// Matches a `Path` against a slice of segment string literals, e.g.
+///
+/// # Examples
+/// ```rust,ignore
+/// match_path_ast(path, &["std", "rt", "begin_unwind"])
+/// ```
+pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
+    path.segments
+        .iter()
+        .rev()
+        .zip(segments.iter().rev())
+        .all(|(a, b)| a.ident.name.as_str() == *b)
+}
+
+/// If the expression is a path to a local, returns the canonical `HirId` of the local.
+pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
+    if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
+        if let Res::Local(id) = path.res {
+            return Some(id);
+        }
+    }
+    None
+}
+
+/// Returns true if the expression is a path to a local with the specified `HirId`.
+/// Use this function to see if an expression matches a function argument or a match binding.
+pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
+    path_to_local(expr) == Some(id)
+}
+
+/// Gets the definition associated to a path.
+#[allow(clippy::shadow_unrelated)] // false positive #6563
+pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
+    macro_rules! try_res {
+        ($e:expr) => {
+            match $e {
+                Some(e) => e,
+                None => return Res::Err,
+            }
+        };
+    }
+    fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export<HirId>> {
+        tcx.item_children(def_id)
+            .iter()
+            .find(|item| item.ident.name.as_str() == name)
+    }
+
+    let (krate, first, path) = match *path {
+        [krate, first, ref path @ ..] => (krate, first, path),
+        _ => return Res::Err,
+    };
+    let tcx = cx.tcx;
+    let crates = tcx.crates();
+    let krate = try_res!(crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate));
+    let first = try_res!(item_child_by_name(tcx, krate.as_def_id(), first));
+    let last = path
+        .iter()
+        .copied()
+        // `get_def_path` seems to generate these empty segments for extern blocks.
+        // We can just ignore them.
+        .filter(|segment| !segment.is_empty())
+        // for each segment, find the child item
+        .try_fold(first, |item, segment| {
+            let def_id = item.res.def_id();
+            if let Some(item) = item_child_by_name(tcx, def_id, segment) {
+                Some(item)
+            } else if matches!(item.res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
+                // it is not a child item so check inherent impl items
+                tcx.inherent_impls(def_id)
+                    .iter()
+                    .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment))
+            } else {
+                None
+            }
+        });
+    try_res!(last).res
+}
+
+/// Convenience function to get the `DefId` of a trait by path.
+/// It could be a trait or trait alias.
+pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
+    match path_to_res(cx, path) {
+        Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
+        _ => None,
+    }
+}
+
+/// Checks whether a type implements a trait.
+/// See also `get_trait_def_id`.
+pub fn implements_trait<'tcx>(
+    cx: &LateContext<'tcx>,
+    ty: Ty<'tcx>,
+    trait_id: DefId,
+    ty_params: &[GenericArg<'tcx>],
+) -> bool {
+    // Do not check on infer_types to avoid panic in evaluate_obligation.
+    if ty.has_infer_types() {
+        return false;
+    }
+    let ty = cx.tcx.erase_regions(ty);
+    if ty.has_escaping_bound_vars() {
+        return false;
+    }
+    let ty_params = cx.tcx.mk_substs(ty_params.iter());
+    cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env))
+}
+
+/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
+///
+/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
+///
+/// ```rust
+/// struct Point(isize, isize);
+///
+/// impl std::ops::Add for Point {
+///     type Output = Self;
+///
+///     fn add(self, other: Self) -> Self {
+///         Point(0, 0)
+///     }
+/// }
+/// ```
+pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx TraitRef<'tcx>> {
+    // Get the implemented trait for the current function
+    let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
+    if_chain! {
+        if parent_impl != hir::CRATE_HIR_ID;
+        if let hir::Node::Item(item) = cx.tcx.hir().get(parent_impl);
+        if let hir::ItemKind::Impl(impl_) = &item.kind;
+        then { return impl_.of_trait.as_ref(); }
+    }
+    None
+}
+
+/// Checks whether this type implements `Drop`.
+pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    match ty.ty_adt_def() {
+        Some(def) => def.has_dtor(cx.tcx),
+        None => false,
+    }
+}
+
+/// Returns the method names and argument list of nested method call expressions that make up
+/// `expr`. method/span lists are sorted with the most recent call first.
+pub fn method_calls<'tcx>(
+    expr: &'tcx Expr<'tcx>,
+    max_depth: usize,
+) -> (Vec<Symbol>, Vec<&'tcx [Expr<'tcx>]>, Vec<Span>) {
+    let mut method_names = Vec::with_capacity(max_depth);
+    let mut arg_lists = Vec::with_capacity(max_depth);
+    let mut spans = Vec::with_capacity(max_depth);
+
+    let mut current = expr;
+    for _ in 0..max_depth {
+        if let ExprKind::MethodCall(path, span, args, _) = &current.kind {
+            if args.iter().any(|e| e.span.from_expansion()) {
+                break;
+            }
+            method_names.push(path.ident.name);
+            arg_lists.push(&**args);
+            spans.push(*span);
+            current = &args[0];
+        } else {
+            break;
+        }
+    }
+
+    (method_names, arg_lists, spans)
+}
+
+/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
+///
+/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
+/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
+/// containing the `Expr`s for
+/// `.bar()` and `.baz()`
+pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<&'a [Expr<'a>]>> {
+    let mut current = expr;
+    let mut matched = Vec::with_capacity(methods.len());
+    for method_name in methods.iter().rev() {
+        // method chains are stored last -> first
+        if let ExprKind::MethodCall(ref path, _, ref args, _) = current.kind {
+            if path.ident.name.as_str() == *method_name {
+                if args.iter().any(|e| e.span.from_expansion()) {
+                    return None;
+                }
+                matched.push(&**args); // build up `matched` backwards
+                current = &args[0] // go to parent expression
+            } else {
+                return None;
+            }
+        } else {
+            return None;
+        }
+    }
+    // Reverse `matched` so that it is in the same order as `methods`.
+    matched.reverse();
+    Some(matched)
+}
+
+/// Returns `true` if the provided `def_id` is an entrypoint to a program.
+pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
+    cx.tcx
+        .entry_fn(LOCAL_CRATE)
+        .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id.to_def_id())
+}
+
+/// Returns `true` if the expression is in the program's `#[panic_handler]`.
+pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+    let parent = cx.tcx.hir().get_parent_item(e.hir_id);
+    let def_id = cx.tcx.hir().local_def_id(parent).to_def_id();
+    Some(def_id) == cx.tcx.lang_items().panic_impl()
+}
+
+/// Gets the name of the item the expression is in, if available.
+pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
+    let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
+    match cx.tcx.hir().find(parent_id) {
+        Some(
+            Node::Item(Item { ident, .. })
+            | Node::TraitItem(TraitItem { ident, .. })
+            | Node::ImplItem(ImplItem { ident, .. }),
+        ) => Some(ident.name),
+        _ => None,
+    }
+}
+
+/// Gets the name of a `Pat`, if any.
+pub fn get_pat_name(pat: &Pat<'_>) -> Option<Symbol> {
+    match pat.kind {
+        PatKind::Binding(.., ref spname, _) => Some(spname.name),
+        PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
+        PatKind::Box(ref p) | PatKind::Ref(ref p, _) => get_pat_name(&*p),
+        _ => None,
+    }
+}
+
+struct ContainsName {
+    name: Symbol,
+    result: bool,
+}
+
+impl<'tcx> Visitor<'tcx> for ContainsName {
+    type Map = Map<'tcx>;
+
+    fn visit_name(&mut self, _: Span, name: Symbol) {
+        if self.name == name {
+            self.result = true;
+        }
+    }
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+}
+
+/// Checks if an `Expr` contains a certain name.
+pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
+    let mut cn = ContainsName { name, result: false };
+    cn.visit_expr(expr);
+    cn.result
+}
+
+/// 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;
+            }
+            if let hir::ExprKind::Ret(..) = &expr.kind {
+                self.found = true;
+            } else {
+                hir::intravisit::walk_expr(self, expr);
+            }
+        }
+
+        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
+    }
+}
+
+/// 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
+}
+
+/// Converts a span to a code snippet if available, otherwise use 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`.
+///
+/// # Example
+/// ```rust,ignore
+/// snippet(cx, expr.span, "..")
+/// ```
+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)
+}
+
+/// Same as `snippet`, but it adapts the applicability level by following rules:
+///
+/// - Applicability level `Unspecified` will never be changed.
+/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
+/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
+/// `HasPlaceholders`
+pub fn snippet_with_applicability<'a, T: LintContext>(
+    cx: &T,
+    span: Span,
+    default: &'a str,
+    applicability: &mut Applicability,
+) -> Cow<'a, str> {
+    if *applicability != Applicability::Unspecified && span.from_expansion() {
+        *applicability = Applicability::MaybeIncorrect;
+    }
+    snippet_opt(cx, span).map_or_else(
+        || {
+            if *applicability == Applicability::MachineApplicable {
+                *applicability = Applicability::HasPlaceholders;
+            }
+            Cow::Borrowed(default)
+        },
+        From::from,
+    )
+}
+
+/// Same as `snippet`, but should only be used when it's clear that the input span is
+/// not a macro argument.
+pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
+    snippet(cx, span.source_callsite(), default)
+}
+
+/// Converts a span to a code snippet. Returns `None` if not available.
+pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
+    cx.sess().source_map().span_to_snippet(span).ok()
+}
+
+/// Converts a span (from a block) to a code snippet if available, otherwise use default.
+///
+/// This trims the code of indentation, except for the first line. Use it for blocks or block-like
+/// things which need to be printed as such.
+///
+/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the
+/// resulting snippet of the given span.
+///
+/// # Example
+///
+/// ```rust,ignore
+/// snippet_block(cx, block.span, "..", None)
+/// // where, `block` is the block of the if expr
+///     if x {
+///         y;
+///     }
+/// // will return the snippet
+/// {
+///     y;
+/// }
+/// ```
+///
+/// ```rust,ignore
+/// snippet_block(cx, block.span, "..", Some(if_expr.span))
+/// // where, `block` is the block of the if expr
+///     if x {
+///         y;
+///     }
+/// // will return the snippet
+/// {
+///         y;
+///     } // aligned with `if`
+/// ```
+/// Note that the first line of the snippet always has 0 indentation.
+pub fn snippet_block<'a, T: LintContext>(
+    cx: &T,
+    span: Span,
+    default: &'a str,
+    indent_relative_to: Option<Span>,
+) -> Cow<'a, str> {
+    let snip = snippet(cx, span, default);
+    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
+    reindent_multiline(snip, true, indent)
+}
+
+/// Same as `snippet_block`, but adapts the applicability level by the rules of
+/// `snippet_with_applicability`.
+pub fn snippet_block_with_applicability<'a, T: LintContext>(
+    cx: &T,
+    span: Span,
+    default: &'a str,
+    indent_relative_to: Option<Span>,
+    applicability: &mut Applicability,
+) -> Cow<'a, str> {
+    let snip = snippet_with_applicability(cx, span, default, applicability);
+    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
+    reindent_multiline(snip, true, indent)
+}
+
+/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
+/// line.
+///
+/// ```rust,ignore
+///     let x = ();
+/// //          ^^
+/// // will be converted to
+///     let x = ();
+/// //  ^^^^^^^^^^
+/// ```
+pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
+    first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
+}
+
+fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
+    let line_span = line_span(cx, span);
+    snippet_opt(cx, line_span).and_then(|snip| {
+        snip.find(|c: char| !c.is_whitespace())
+            .map(|pos| line_span.lo() + BytePos::from_usize(pos))
+    })
+}
+
+/// Returns the indentation of the line of a span
+///
+/// ```rust,ignore
+/// let x = ();
+/// //      ^^ -- will return 0
+///     let x = ();
+/// //          ^^ -- will return 4
+/// ```
+pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
+    snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
+}
+
+/// Returns the positon just before rarrow
+///
+/// ```rust,ignore
+/// fn into(self) -> () {}
+///              ^
+/// // in case of unformatted code
+/// fn into2(self)-> () {}
+///               ^
+/// fn into3(self)   -> () {}
+///               ^
+/// ```
+pub fn position_before_rarrow(s: &str) -> Option<usize> {
+    s.rfind("->").map(|rpos| {
+        let mut rpos = rpos;
+        let chars: Vec<char> = s.chars().collect();
+        while rpos > 1 {
+            if let Some(c) = chars.get(rpos - 1) {
+                if c.is_whitespace() {
+                    rpos -= 1;
+                    continue;
+                }
+            }
+            break;
+        }
+        rpos
+    })
+}
+
+/// Extends the span to the beginning of the spans line, incl. whitespaces.
+///
+/// ```rust,ignore
+///        let x = ();
+/// //             ^^
+/// // will be converted to
+///        let x = ();
+/// // ^^^^^^^^^^^^^^
+/// ```
+fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
+    let span = original_sp(span, DUMMY_SP);
+    let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap();
+    let line_no = source_map_and_line.line;
+    let line_start = source_map_and_line.sf.lines[line_no];
+    Span::new(line_start, span.hi(), span.ctxt())
+}
+
+/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
+/// Also takes an `Option<String>` which can be put inside the braces.
+pub fn expr_block<'a, T: LintContext>(
+    cx: &T,
+    expr: &Expr<'_>,
+    option: Option<String>,
+    default: &'a str,
+    indent_relative_to: Option<Span>,
+) -> Cow<'a, str> {
+    let code = snippet_block(cx, expr.span, default, indent_relative_to);
+    let string = option.unwrap_or_default();
+    if expr.span.from_expansion() {
+        Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default)))
+    } else if let ExprKind::Block(_, _) = expr.kind {
+        Cow::Owned(format!("{}{}", code, string))
+    } else if string.is_empty() {
+        Cow::Owned(format!("{{ {} }}", code))
+    } else {
+        Cow::Owned(format!("{{\n{};\n{}\n}}", code, string))
+    }
+}
+
+/// Reindent a multiline string with possibility of ignoring the first line.
+#[allow(clippy::needless_pass_by_value)]
+pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
+    let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' ');
+    let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t');
+    reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into()
+}
+
+fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
+    let x = s
+        .lines()
+        .skip(ignore_first as usize)
+        .filter_map(|l| {
+            if l.is_empty() {
+                None
+            } else {
+                // ignore empty lines
+                Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0)
+            }
+        })
+        .min()
+        .unwrap_or(0);
+    let indent = indent.unwrap_or(0);
+    s.lines()
+        .enumerate()
+        .map(|(i, l)| {
+            if (ignore_first && i == 0) || l.is_empty() {
+                l.to_owned()
+            } else if x > indent {
+                l.split_at(x - indent).1.to_owned()
+            } else {
+                " ".repeat(indent - x) + l
+            }
+        })
+        .collect::<Vec<String>>()
+        .join("\n")
+}
+
+/// Gets the parent expression, if any –- this is useful to constrain a lint.
+pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    let map = &cx.tcx.hir();
+    let hir_id = e.hir_id;
+    let parent_id = map.get_parent_node(hir_id);
+    if hir_id == parent_id {
+        return None;
+    }
+    map.find(parent_id).and_then(|node| {
+        if let Node::Expr(parent) = node {
+            Some(parent)
+        } else {
+            None
+        }
+    })
+}
+
+pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
+    let map = &cx.tcx.hir();
+    let enclosing_node = map
+        .get_enclosing_scope(hir_id)
+        .and_then(|enclosing_id| map.find(enclosing_id));
+    enclosing_node.and_then(|node| match node {
+        Node::Block(block) => Some(block),
+        Node::Item(&Item {
+            kind: ItemKind::Fn(_, _, eid),
+            ..
+        })
+        | Node::ImplItem(&ImplItem {
+            kind: ImplItemKind::Fn(_, eid),
+            ..
+        }) => match cx.tcx.hir().body(eid).value.kind {
+            ExprKind::Block(ref block, _) => Some(block),
+            _ => None,
+        },
+        _ => None,
+    })
+}
+
+/// Returns the base type for HIR references and pointers.
+pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
+    match ty.kind {
+        TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(&mut_ty.ty),
+        _ => ty,
+    }
+}
+
+/// Returns the base type for references and raw pointers, and count reference
+/// depth.
+pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
+    fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
+        match ty.kind() {
+            ty::Ref(_, ty, _) => inner(ty, depth + 1),
+            _ => (ty, depth),
+        }
+    }
+    inner(ty, 0)
+}
+
+/// Checks whether the given expression is a constant integer of the given value.
+/// unlike `is_integer_literal`, this version does const folding
+pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
+    if is_integer_literal(e, value) {
+        return true;
+    }
+    let map = cx.tcx.hir();
+    let parent_item = map.get_parent_item(e.hir_id);
+    if let Some((Constant::Int(v), _)) = map
+        .maybe_body_owned_by(parent_item)
+        .and_then(|body_id| constant(cx, cx.tcx.typeck_body(body_id), e))
+    {
+        value == v
+    } else {
+        false
+    }
+}
+
+/// Checks whether the given expression is a constant literal of the given value.
+pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
+    // FIXME: use constant folding
+    if let ExprKind::Lit(ref spanned) = expr.kind {
+        if let LitKind::Int(v, _) = spanned.node {
+            return v == value;
+        }
+    }
+    false
+}
+
+/// Returns `true` if the given `Expr` has been coerced before.
+///
+/// Examples of coercions can be found in the Nomicon at
+/// <https://doc.rust-lang.org/nomicon/coercions.html>.
+///
+/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_typeck::check::coercion` for more
+/// information on adjustments and coercions.
+pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+    cx.typeck_results().adjustments().get(e.hir_id).is_some()
+}
+
+/// Returns the pre-expansion span if is this comes from an expansion of the
+/// macro `name`.
+/// See also `is_direct_expn_of`.
+#[must_use]
+pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
+    loop {
+        if span.from_expansion() {
+            let data = span.ctxt().outer_expn_data();
+            let new_span = data.call_site;
+
+            if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
+                if mac_name.as_str() == name {
+                    return Some(new_span);
+                }
+            }
+
+            span = new_span;
+        } else {
+            return None;
+        }
+    }
+}
+
+/// Returns the pre-expansion span if the span directly comes from an expansion
+/// of the macro `name`.
+/// The difference with `is_expn_of` is that in
+/// ```rust,ignore
+/// foo!(bar!(42));
+/// ```
+/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
+/// `bar!` by
+/// `is_direct_expn_of`.
+#[must_use]
+pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
+    if span.from_expansion() {
+        let data = span.ctxt().outer_expn_data();
+        let new_span = data.call_site;
+
+        if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
+            if mac_name.as_str() == name {
+                return Some(new_span);
+            }
+        }
+    }
+
+    None
+}
+
+/// Convenience function to get the return type of a function.
+pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
+    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
+    let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
+    cx.tcx.erase_late_bound_regions(ret_ty)
+}
+
+/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
+pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool {
+    ty.walk().any(|inner| match inner.unpack() {
+        GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty),
+        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
+    })
+}
+
+/// Returns `true` if the given type is an `unsafe` function.
+pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    match ty.kind() {
+        ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
+        _ => false,
+    }
+}
+
+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)
+}
+
+/// Checks if an expression is constructing a tuple-like enum variant or struct
+pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    if let ExprKind::Call(ref fun, _) = expr.kind {
+        if let ExprKind::Path(ref qp) = fun.kind {
+            let res = cx.qpath_res(qp, fun.hir_id);
+            return match res {
+                def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
+                def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
+                _ => false,
+            };
+        }
+    }
+    false
+}
+
+/// Returns `true` if a pattern is refutable.
+// TODO: should be implemented using rustc/mir_build/thir machinery
+pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
+    fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
+        matches!(
+            cx.qpath_res(qpath, id),
+            def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
+        )
+    }
+
+    fn are_refutable<'a, I: Iterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, mut i: I) -> bool {
+        i.any(|pat| is_refutable(cx, pat))
+    }
+
+    match pat.kind {
+        PatKind::Wild => false,
+        PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
+        PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => is_refutable(cx, pat),
+        PatKind::Lit(..) | PatKind::Range(..) => true,
+        PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
+        PatKind::Or(ref pats) => {
+            // TODO: should be the honest check, that pats is exhaustive set
+            are_refutable(cx, pats.iter().map(|pat| &**pat))
+        },
+        PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
+        PatKind::Struct(ref qpath, ref fields, _) => {
+            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat))
+        },
+        PatKind::TupleStruct(ref qpath, ref pats, _) => {
+            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat))
+        },
+        PatKind::Slice(ref head, ref middle, ref tail) => {
+            match &cx.typeck_results().node_type(pat.hir_id).kind() {
+                ty::Slice(..) => {
+                    // [..] is the only irrefutable slice pattern.
+                    !head.is_empty() || middle.is_none() || !tail.is_empty()
+                },
+                ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)),
+                _ => {
+                    // unreachable!()
+                    true
+                },
+            }
+        },
+    }
+}
+
+/// 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))
+}
+
+/// Remove blocks around an expression.
+///
+/// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return
+/// themselves.
+pub fn remove_blocks<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
+    while let ExprKind::Block(ref block, ..) = expr.kind {
+        match (block.stmts.is_empty(), block.expr.as_ref()) {
+            (true, Some(e)) => expr = e,
+            _ => break,
+        }
+    }
+    expr
+}
+
+pub fn is_self(slf: &Param<'_>) -> bool {
+    if let PatKind::Binding(.., name, _) = slf.pat.kind {
+        name.name == kw::SelfLower
+    } else {
+        false
+    }
+}
+
+pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
+    if_chain! {
+        if let TyKind::Path(QPath::Resolved(None, ref path)) = slf.kind;
+        if let Res::SelfTy(..) = path.res;
+        then {
+            return true
+        }
+    }
+    false
+}
+
+pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
+    (0..decl.inputs.len()).map(move |i| &body.params[i])
+}
+
+/// Checks if a given expression is a match expression expanded from the `?`
+/// operator or the `try` macro.
+pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+    fn is_ok(arm: &Arm<'_>) -> bool {
+        if_chain! {
+            if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pat.kind;
+            if match_qpath(path, &paths::RESULT_OK[1..]);
+            if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
+            if path_to_local_id(arm.body, hir_id);
+            then {
+                return true;
+            }
+        }
+        false
+    }
+
+    fn is_err(arm: &Arm<'_>) -> bool {
+        if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
+            match_qpath(path, &paths::RESULT_ERR[1..])
+        } else {
+            false
+        }
+    }
+
+    if let ExprKind::Match(_, ref arms, ref source) = expr.kind {
+        // desugared from a `?` operator
+        if let MatchSource::TryDesugar = *source {
+            return Some(expr);
+        }
+
+        if_chain! {
+            if arms.len() == 2;
+            if arms[0].guard.is_none();
+            if arms[1].guard.is_none();
+            if (is_ok(&arms[0]) && is_err(&arms[1])) ||
+                (is_ok(&arms[1]) && is_err(&arms[0]));
+            then {
+                return Some(expr);
+            }
+        }
+    }
+
+    None
+}
+
+/// Returns `true` if the lint is allowed in the current context
+///
+/// Useful for skipping long running code when it's unnecessary
+pub fn is_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
+    cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
+}
+
+pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
+    while let PatKind::Ref(subpat, _) = pat.kind {
+        pat = subpat;
+    }
+    pat
+}
+
+pub fn int_bits(tcx: TyCtxt<'_>, ity: ty::IntTy) -> u64 {
+    Integer::from_int_ty(&tcx, ity).size().bits()
+}
+
+#[allow(clippy::cast_possible_wrap)]
+/// Turn a constant int byte representation into an i128
+pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: ty::IntTy) -> i128 {
+    let amt = 128 - int_bits(tcx, ity);
+    ((u as i128) << amt) >> amt
+}
+
+#[allow(clippy::cast_sign_loss)]
+/// clip unused bytes
+pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: ty::IntTy) -> u128 {
+    let amt = 128 - int_bits(tcx, ity);
+    ((u as u128) << amt) >> amt
+}
+
+/// clip unused bytes
+pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: ty::UintTy) -> u128 {
+    let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
+    let amt = 128 - bits;
+    (u << amt) >> amt
+}
+
+/// Removes block comments from the given `Vec` of lines.
+///
+/// # Examples
+///
+/// ```rust,ignore
+/// without_block_comments(vec!["/*", "foo", "*/"]);
+/// // => vec![]
+///
+/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
+/// // => vec!["bar"]
+/// ```
+pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
+    let mut without = vec![];
+
+    let mut nest_level = 0;
+
+    for line in lines {
+        if line.contains("/*") {
+            nest_level += 1;
+            continue;
+        } else if line.contains("*/") {
+            nest_level -= 1;
+            continue;
+        }
+
+        if nest_level == 0 {
+            without.push(line);
+        }
+    }
+
+    without
+}
+
+pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> 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)) {
+            return true;
+        }
+        prev_enclosing_node = Some(enclosing_node);
+        enclosing_node = map.get_parent_item(enclosing_node);
+    }
+    false
+}
+
+/// Returns true if ty has `iter` or `iter_mut` methods
+pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<&'static str> {
+    // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
+    // exists and has the desired signature. Unfortunately FnCtxt is not exported
+    // so we can't use its `lookup_method` method.
+    let into_iter_collections: [&[&str]; 13] = [
+        &paths::VEC,
+        &paths::OPTION,
+        &paths::RESULT,
+        &paths::BTREESET,
+        &paths::BTREEMAP,
+        &paths::VEC_DEQUE,
+        &paths::LINKED_LIST,
+        &paths::BINARY_HEAP,
+        &paths::HASHSET,
+        &paths::HASHMAP,
+        &paths::PATH_BUF,
+        &paths::PATH,
+        &paths::RECEIVER,
+    ];
+
+    let ty_to_check = match probably_ref_ty.kind() {
+        ty::Ref(_, ty_to_check, _) => ty_to_check,
+        _ => probably_ref_ty,
+    };
+
+    let def_id = match ty_to_check.kind() {
+        ty::Array(..) => return Some("array"),
+        ty::Slice(..) => return Some("slice"),
+        ty::Adt(adt, _) => adt.did,
+        _ => return None,
+    };
+
+    for path in &into_iter_collections {
+        if match_def_path(cx, def_id, path) {
+            return Some(*path.last().unwrap());
+        }
+    }
+    None
+}
+
+/// Matches a function call with the given path and returns the arguments.
+///
+/// Usage:
+///
+/// ```rust,ignore
+/// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
+/// ```
+pub fn match_function_call<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    path: &[&str],
+) -> Option<&'tcx [Expr<'tcx>]> {
+    if_chain! {
+        if let ExprKind::Call(ref fun, ref args) = expr.kind;
+        if let ExprKind::Path(ref qpath) = fun.kind;
+        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
+        if match_def_path(cx, fun_def_id, path);
+        then {
+            return Some(&args)
+        }
+    };
+    None
+}
+
+/// Checks if `Ty` is normalizable. This function is useful
+/// to avoid crashes on `layout_of`.
+pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
+    cx.tcx.infer_ctxt().enter(|infcx| {
+        let cause = rustc_middle::traits::ObligationCause::dummy();
+        infcx.at(&cause, param_env).normalize(ty).is_ok()
+    })
+}
+
+pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
+    // We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path`
+    // accepts only that. We should probably move to Symbols in Clippy as well.
+    let syms = syms.iter().map(|p| Symbol::intern(p)).collect::<Vec<Symbol>>();
+    cx.match_def_path(did, &syms)
+}
+
+pub fn match_panic_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx [Expr<'tcx>]> {
+    match_function_call(cx, expr, &paths::BEGIN_PANIC)
+        .or_else(|| match_function_call(cx, expr, &paths::BEGIN_PANIC_FMT))
+        .or_else(|| match_function_call(cx, expr, &paths::PANIC_ANY))
+        .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC))
+        .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_FMT))
+        .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_STR))
+}
+
+pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool {
+    match_def_path(cx, did, &paths::BEGIN_PANIC)
+        || match_def_path(cx, did, &paths::BEGIN_PANIC_FMT)
+        || match_def_path(cx, did, &paths::PANIC_ANY)
+        || match_def_path(cx, did, &paths::PANICKING_PANIC)
+        || match_def_path(cx, did, &paths::PANICKING_PANIC_FMT)
+        || match_def_path(cx, did, &paths::PANICKING_PANIC_STR)
+}
+
+/// Returns the list of condition expressions and the list of blocks in a
+/// sequence of `if/else`.
+/// E.g., this returns `([a, b], [c, d, e])` for the expression
+/// `if a { c } else if b { d } else { e }`.
+pub fn if_sequence<'tcx>(
+    mut expr: &'tcx Expr<'tcx>,
+) -> (SmallVec<[&'tcx Expr<'tcx>; 1]>, SmallVec<[&'tcx Block<'tcx>; 1]>) {
+    let mut conds = SmallVec::new();
+    let mut blocks: SmallVec<[&Block<'_>; 1]> = SmallVec::new();
+
+    while let ExprKind::If(ref cond, ref then_expr, ref else_expr) = expr.kind {
+        conds.push(&**cond);
+        if let ExprKind::Block(ref block, _) = then_expr.kind {
+            blocks.push(block);
+        } else {
+            panic!("ExprKind::If node is not an ExprKind::Block");
+        }
+
+        if let Some(ref else_expr) = *else_expr {
+            expr = else_expr;
+        } else {
+            break;
+        }
+    }
+
+    // final `else {..}`
+    if !blocks.is_empty() {
+        if let ExprKind::Block(ref block, _) = expr.kind {
+            blocks.push(&**block);
+        }
+    }
+
+    (conds, blocks)
+}
+
+pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
+    let map = cx.tcx.hir();
+    let parent_id = map.get_parent_node(expr.hir_id);
+    let parent_node = map.get(parent_id);
+    matches!(
+        parent_node,
+        Node::Expr(Expr {
+            kind: ExprKind::If(_, _, _),
+            ..
+        })
+    )
+}
+
+// Finds the attribute with the given name, if any
+pub fn attr_by_name<'a>(attrs: &'a [Attribute], name: &'_ str) -> Option<&'a Attribute> {
+    attrs
+        .iter()
+        .find(|attr| attr.ident().map_or(false, |ident| ident.as_str() == name))
+}
+
+// Finds the `#[must_use]` attribute, if any
+pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
+    attr_by_name(attrs, "must_use")
+}
+
+// Returns whether the type has #[must_use] attribute
+pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    match ty.kind() {
+        ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(),
+        ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(),
+        ty::Slice(ref ty)
+        | ty::Array(ref ty, _)
+        | ty::RawPtr(ty::TypeAndMut { ref ty, .. })
+        | ty::Ref(_, ref ty, _) => {
+            // for the Array case we don't need to care for the len == 0 case
+            // because we don't want to lint functions returning empty arrays
+            is_must_use_ty(cx, *ty)
+        },
+        ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)),
+        ty::Opaque(ref def_id, _) => {
+            for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
+                if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() {
+                    if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() {
+                        return true;
+                    }
+                }
+            }
+            false
+        },
+        ty::Dynamic(binder, _) => {
+            for predicate in binder.iter() {
+                if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
+                    if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
+                        return true;
+                    }
+                }
+            }
+            false
+        },
+        _ => false,
+    }
+}
+
+// check if expr is calling method or function with #[must_use] attribute
+pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    let did = match expr.kind {
+        ExprKind::Call(ref path, _) => if_chain! {
+            if let ExprKind::Path(ref qpath) = path.kind;
+            if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
+            then {
+                Some(did)
+            } else {
+                None
+            }
+        },
+        ExprKind::MethodCall(_, _, _, _) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
+        _ => None,
+    };
+
+    did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some())
+}
+
+pub fn is_no_std_crate(krate: &Crate<'_>) -> bool {
+    krate.item.attrs.iter().any(|attr| {
+        if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
+            attr.path == sym::no_std
+        } else {
+            false
+        }
+    })
+}
+
+/// Check if parent of a hir node is a trait implementation block.
+/// For example, `f` in
+/// ```rust,ignore
+/// impl Trait for S {
+///     fn f() {}
+/// }
+/// ```
+pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
+    if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
+        matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
+    } else {
+        false
+    }
+}
+
+/// Check if it's even possible to satisfy the `where` clause for the item.
+///
+/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
+///
+/// ```ignore
+/// fn foo() where i32: Iterator {
+///     for _ in 2i32 {}
+/// }
+/// ```
+pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
+    use rustc_trait_selection::traits;
+    let predicates =
+        cx.tcx
+            .predicates_of(did)
+            .predicates
+            .iter()
+            .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
+    traits::impossible_predicates(
+        cx.tcx,
+        traits::elaborate_predicates(cx.tcx, predicates)
+            .map(|o| o.predicate)
+            .collect::<Vec<_>>(),
+    )
+}
+
+/// Returns the `DefId` of the callee if the given expression is a function or method call.
+pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
+    match &expr.kind {
+        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
+        ExprKind::Call(
+            Expr {
+                kind: ExprKind::Path(qpath),
+                hir_id: path_hir_id,
+                ..
+            },
+            ..,
+        ) => cx.typeck_results().qpath_res(qpath, *path_hir_id).opt_def_id(),
+        _ => None,
+    }
+}
+
+pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool {
+    lints.iter().any(|lint| {
+        matches!(
+            cx.tcx.lint_level_at_node(lint, id),
+            (Level::Forbid | Level::Deny | Level::Warn, _)
+        )
+    })
+}
+
+/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point
+/// number type, a str, or an array, slice, or tuple of those types).
+pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
+    match ty.kind() {
+        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
+        ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
+        ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
+        ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type),
+        _ => false,
+    }
+}
+
+/// Returns Option<String> where String is a textual representation of the type encapsulated in the
+/// slice iff the given expression is a slice of primitives (as defined in the
+/// `is_recursively_primitive_type` function) and None otherwise.
+pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
+    let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
+    let expr_kind = expr_type.kind();
+    let is_primitive = match expr_kind {
+        ty::Slice(element_type) => is_recursively_primitive_type(element_type),
+        ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &ty::Slice(_)) => {
+            if let ty::Slice(element_type) = inner_ty.kind() {
+                is_recursively_primitive_type(element_type)
+            } else {
+                unreachable!()
+            }
+        },
+        _ => false,
+    };
+
+    if is_primitive {
+        // if we have wrappers like Array, Slice or Tuple, print these
+        // and get the type enclosed in the slice ref
+        match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
+            ty::Slice(..) => return Some("slice".into()),
+            ty::Array(..) => return Some("array".into()),
+            ty::Tuple(..) => return Some("tuple".into()),
+            _ => {
+                // is_recursively_primitive_type() should have taken care
+                // of the rest and we can rely on the type that is found
+                let refs_peeled = expr_type.peel_refs();
+                return Some(refs_peeled.walk().last().unwrap().to_string());
+            },
+        }
+    }
+    None
+}
+
+/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
+/// `hash` must be comformed with `eq`
+pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
+where
+    Hash: Fn(&T) -> u64,
+    Eq: Fn(&T, &T) -> bool,
+{
+    if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) {
+        return vec![(&exprs[0], &exprs[1])];
+    }
+
+    let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
+
+    let mut map: FxHashMap<_, Vec<&_>> =
+        FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
+
+    for expr in exprs {
+        match map.entry(hash(expr)) {
+            Entry::Occupied(mut o) => {
+                for o in o.get() {
+                    if eq(o, expr) {
+                        match_expr_list.push((o, expr));
+                    }
+                }
+                o.get_mut().push(expr);
+            },
+            Entry::Vacant(v) => {
+                v.insert(vec![expr]);
+            },
+        }
+    }
+
+    match_expr_list
+}
+
+/// Peels off all references on the pattern. Returns the underlying pattern and the number of
+/// references removed.
+pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
+    fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
+        if let PatKind::Ref(pat, _) = pat.kind {
+            peel(pat, count + 1)
+        } else {
+            (pat, count)
+        }
+    }
+    peel(pat, 0)
+}
+
+/// Peels off up to the given number of references on the expression. Returns the underlying
+/// expression and the number of references removed.
+pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
+    fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) {
+        match expr.kind {
+            ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target),
+            _ => (expr, count),
+        }
+    }
+    f(expr, 0, count)
+}
+
+/// Peels off all references on the expression. Returns the underlying expression and the number of
+/// references removed.
+pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
+    fn f(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
+        match expr.kind {
+            ExprKind::AddrOf(BorrowKind::Ref, _, expr) => f(expr, count + 1),
+            _ => (expr, count),
+        }
+    }
+    f(expr, 0)
+}
+
+/// Peels off all references on the type. Returns the underlying type and the number of references
+/// removed.
+pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
+    fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
+        if let ty::Ref(_, ty, _) = ty.kind() {
+            peel(ty, count + 1)
+        } else {
+            (ty, count)
+        }
+    }
+    peel(ty, 0)
+}
+
+/// Peels off all references on the type.Returns the underlying type, the number of references
+/// removed, and whether the pointer is ultimately mutable or not.
+pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
+    fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
+        match ty.kind() {
+            ty::Ref(_, ty, Mutability::Mut) => f(ty, count + 1, mutability),
+            ty::Ref(_, ty, Mutability::Not) => f(ty, count + 1, Mutability::Not),
+            _ => (ty, count, mutability),
+        }
+    }
+    f(ty, 0, Mutability::Mut)
+}
+
+#[macro_export]
+macro_rules! unwrap_cargo_metadata {
+    ($cx: ident, $lint: ident, $deps: expr) => {{
+        let mut command = cargo_metadata::MetadataCommand::new();
+        if !$deps {
+            command.no_deps();
+        }
+
+        match command.exec() {
+            Ok(metadata) => metadata,
+            Err(err) => {
+                span_lint($cx, $lint, DUMMY_SP, &format!("could not read cargo metadata: {}", err));
+                return;
+            },
+        }
+    }};
+}
+
+pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
+    if_chain! {
+        if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
+        if let Res::Def(_, def_id) = path.res;
+        then {
+            cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr)
+        } else {
+            false
+        }
+    }
+}
+
+/// Check if the resolution of a given path is an `Ok` variant of `Result`.
+pub fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool {
+    if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() {
+        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
+            if let Some(variant_id) = cx.tcx.parent(id) {
+                return variant_id == ok_id;
+            }
+        }
+    }
+    false
+}
+
+/// Check if the resolution of a given path is a `Some` variant of `Option`.
+pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool {
+    if let Some(some_id) = cx.tcx.lang_items().option_some_variant() {
+        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
+            if let Some(variant_id) = cx.tcx.parent(id) {
+                return variant_id == some_id;
+            }
+        }
+    }
+    false
+}
+
+#[cfg(test)]
+mod test {
+    use super::{reindent_multiline, without_block_comments};
+
+    #[test]
+    fn test_reindent_multiline_single_line() {
+        assert_eq!("", reindent_multiline("".into(), false, None));
+        assert_eq!("...", reindent_multiline("...".into(), false, None));
+        assert_eq!("...", reindent_multiline("    ...".into(), false, None));
+        assert_eq!("...", reindent_multiline("\t...".into(), false, None));
+        assert_eq!("...", reindent_multiline("\t\t...".into(), false, None));
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn test_reindent_multiline_block() {
+        assert_eq!("\
+    if x {
+        y
+    } else {
+        z
+    }", reindent_multiline("    if x {
+            y
+        } else {
+            z
+        }".into(), false, None));
+        assert_eq!("\
+    if x {
+    \ty
+    } else {
+    \tz
+    }", reindent_multiline("    if x {
+        \ty
+        } else {
+        \tz
+        }".into(), false, None));
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn test_reindent_multiline_empty_line() {
+        assert_eq!("\
+    if x {
+        y
+
+    } else {
+        z
+    }", reindent_multiline("    if x {
+            y
+
+        } else {
+            z
+        }".into(), false, None));
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn test_reindent_multiline_lines_deeper() {
+        assert_eq!("\
+        if x {
+            y
+        } else {
+            z
+        }", reindent_multiline("\
+    if x {
+        y
+    } else {
+        z
+    }".into(), true, Some(8)));
+    }
+
+    #[test]
+    fn test_without_block_comments_lines_without_block_comments() {
+        let result = without_block_comments(vec!["/*", "", "*/"]);
+        println!("result: {:?}", result);
+        assert!(result.is_empty());
+
+        let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
+        assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
+
+        let result = without_block_comments(vec!["/* rust", "", "*/"]);
+        assert!(result.is_empty());
+
+        let result = without_block_comments(vec!["/* one-line comment */"]);
+        assert!(result.is_empty());
+
+        let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
+        assert!(result.is_empty());
+
+        let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
+        assert!(result.is_empty());
+
+        let result = without_block_comments(vec!["foo", "bar", "baz"]);
+        assert_eq!(result, vec!["foo", "bar", "baz"]);
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/numeric_literal.rs b/src/tools/clippy/clippy_utils/src/numeric_literal.rs
new file mode 100644 (file)
index 0000000..d02603d
--- /dev/null
@@ -0,0 +1,228 @@
+use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
+
+#[derive(Debug, PartialEq, Copy, Clone)]
+pub enum Radix {
+    Binary,
+    Octal,
+    Decimal,
+    Hexadecimal,
+}
+
+impl Radix {
+    /// Returns a reasonable digit group size for this radix.
+    #[must_use]
+    fn suggest_grouping(self) -> usize {
+        match self {
+            Self::Binary | Self::Hexadecimal => 4,
+            Self::Octal | Self::Decimal => 3,
+        }
+    }
+}
+
+/// A helper method to format numeric literals with digit grouping.
+/// `lit` must be a valid numeric literal without suffix.
+pub fn format(lit: &str, type_suffix: Option<&str>, float: bool) -> String {
+    NumericLiteral::new(lit, type_suffix, float).format()
+}
+
+#[derive(Debug)]
+pub struct NumericLiteral<'a> {
+    /// Which radix the literal was represented in.
+    pub radix: Radix,
+    /// The radix prefix, if present.
+    pub prefix: Option<&'a str>,
+
+    /// The integer part of the number.
+    pub integer: &'a str,
+    /// The fraction part of the number.
+    pub fraction: Option<&'a str>,
+    /// The exponent separator (b'e' or b'E') including preceding underscore if present
+    /// and the exponent part.
+    pub exponent: Option<(&'a str, &'a str)>,
+
+    /// The type suffix, including preceding underscore if present.
+    pub suffix: Option<&'a str>,
+}
+
+impl<'a> NumericLiteral<'a> {
+    pub fn from_lit(src: &'a str, lit: &Lit) -> Option<NumericLiteral<'a>> {
+        NumericLiteral::from_lit_kind(src, &lit.kind)
+    }
+
+    pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
+        if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) {
+            let (unsuffixed, suffix) = split_suffix(&src, lit_kind);
+            let float = matches!(lit_kind, LitKind::Float(..));
+            Some(NumericLiteral::new(unsuffixed, suffix, float))
+        } else {
+            None
+        }
+    }
+
+    #[must_use]
+    pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self {
+        // Determine delimiter for radix prefix, if present, and radix.
+        let radix = if lit.starts_with("0x") {
+            Radix::Hexadecimal
+        } else if lit.starts_with("0b") {
+            Radix::Binary
+        } else if lit.starts_with("0o") {
+            Radix::Octal
+        } else {
+            Radix::Decimal
+        };
+
+        // Grab part of the literal after prefix, if present.
+        let (prefix, mut sans_prefix) = if let Radix::Decimal = radix {
+            (None, lit)
+        } else {
+            let (p, s) = lit.split_at(2);
+            (Some(p), s)
+        };
+
+        if suffix.is_some() && sans_prefix.ends_with('_') {
+            // The '_' before the suffix isn't part of the digits
+            sans_prefix = &sans_prefix[..sans_prefix.len() - 1];
+        }
+
+        let (integer, fraction, exponent) = Self::split_digit_parts(sans_prefix, float);
+
+        Self {
+            radix,
+            prefix,
+            integer,
+            fraction,
+            exponent,
+            suffix,
+        }
+    }
+
+    pub fn is_decimal(&self) -> bool {
+        self.radix == Radix::Decimal
+    }
+
+    pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) {
+        let mut integer = digits;
+        let mut fraction = None;
+        let mut exponent = None;
+
+        if float {
+            for (i, c) in digits.char_indices() {
+                match c {
+                    '.' => {
+                        integer = &digits[..i];
+                        fraction = Some(&digits[i + 1..]);
+                    },
+                    'e' | 'E' => {
+                        let exp_start = if digits[..i].ends_with('_') { i - 1 } else { i };
+
+                        if integer.len() > exp_start {
+                            integer = &digits[..exp_start];
+                        } else {
+                            fraction = Some(&digits[integer.len() + 1..exp_start]);
+                        };
+                        exponent = Some((&digits[exp_start..=i], &digits[i + 1..]));
+                        break;
+                    },
+                    _ => {},
+                }
+            }
+        }
+
+        (integer, fraction, exponent)
+    }
+
+    /// Returns literal formatted in a sensible way.
+    pub fn format(&self) -> String {
+        let mut output = String::new();
+
+        if let Some(prefix) = self.prefix {
+            output.push_str(prefix);
+        }
+
+        let group_size = self.radix.suggest_grouping();
+
+        Self::group_digits(
+            &mut output,
+            self.integer,
+            group_size,
+            true,
+            self.radix == Radix::Hexadecimal,
+        );
+
+        if let Some(fraction) = self.fraction {
+            output.push('.');
+            Self::group_digits(&mut output, fraction, group_size, false, false);
+        }
+
+        if let Some((separator, exponent)) = self.exponent {
+            output.push_str(separator);
+            Self::group_digits(&mut output, exponent, group_size, true, false);
+        }
+
+        if let Some(suffix) = self.suffix {
+            output.push('_');
+            output.push_str(suffix);
+        }
+
+        output
+    }
+
+    pub fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, pad: bool) {
+        debug_assert!(group_size > 0);
+
+        let mut digits = input.chars().filter(|&c| c != '_');
+
+        let first_group_size;
+
+        if partial_group_first {
+            first_group_size = (digits.clone().count() - 1) % group_size + 1;
+            if pad {
+                for _ in 0..group_size - first_group_size {
+                    output.push('0');
+                }
+            }
+        } else {
+            first_group_size = group_size;
+        }
+
+        for _ in 0..first_group_size {
+            if let Some(digit) = digits.next() {
+                output.push(digit);
+            }
+        }
+
+        for (c, i) in digits.zip((0..group_size).cycle()) {
+            if i == 0 {
+                output.push('_');
+            }
+            output.push(c);
+        }
+    }
+}
+
+fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) {
+    debug_assert!(lit_kind.is_numeric());
+    lit_suffix_length(lit_kind).map_or((src, None), |suffix_length| {
+        let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length);
+        (unsuffixed, Some(suffix))
+    })
+}
+
+fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> {
+    debug_assert!(lit_kind.is_numeric());
+    let suffix = match lit_kind {
+        LitKind::Int(_, int_lit_kind) => match int_lit_kind {
+            LitIntType::Signed(int_ty) => Some(int_ty.name_str()),
+            LitIntType::Unsigned(uint_ty) => Some(uint_ty.name_str()),
+            LitIntType::Unsuffixed => None,
+        },
+        LitKind::Float(_, float_lit_kind) => match float_lit_kind {
+            LitFloatType::Suffixed(float_ty) => Some(float_ty.name_str()),
+            LitFloatType::Unsuffixed => None,
+        },
+        _ => None,
+    };
+
+    suffix.map(str::len)
+}
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
new file mode 100644 (file)
index 0000000..e617867
--- /dev/null
@@ -0,0 +1,181 @@
+//! This module contains paths to types and functions Clippy needs to know
+//! about.
+//!
+//! Whenever possible, please consider diagnostic items over hardcoded paths.
+//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
+
+pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
+pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
+pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
+pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
+pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
+pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"];
+pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
+pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
+pub const BOX: [&str; 3] = ["alloc", "boxed", "Box"];
+pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
+pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
+pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
+pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"];
+pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
+pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
+pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
+pub const COPY: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"];
+pub const COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy"];
+pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
+pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"];
+pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
+pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
+pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
+pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
+pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
+pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
+pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
+pub const DROP: [&str; 3] = ["core", "mem", "drop"];
+pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
+#[cfg(feature = "internal-lints")]
+pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
+pub const EXIT: [&str; 3] = ["std", "process", "exit"];
+pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
+pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
+pub const FILE: [&str; 3] = ["std", "fs", "File"];
+pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
+pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
+pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
+pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
+pub const FN: [&str; 3] = ["core", "ops", "Fn"];
+pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"];
+pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"];
+pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
+pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
+pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
+pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
+pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
+pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
+pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
+#[cfg(feature = "internal-lints")]
+pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
+#[cfg(feature = "internal-lints")]
+pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
+pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
+pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
+pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
+pub const INTO: [&str; 3] = ["core", "convert", "Into"];
+pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
+pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
+pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
+pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"];
+pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"];
+pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
+#[cfg(feature = "internal-lints")]
+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 LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
+#[cfg(feature = "internal-lints")]
+pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
+pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
+pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
+pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "ManuallyDrop"];
+pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"];
+pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"];
+pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
+pub const MEM_SIZE_OF: [&str; 3] = ["core", "mem", "size_of"];
+pub const MEM_SIZE_OF_VAL: [&str; 3] = ["core", "mem", "size_of_val"];
+pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
+pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
+pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
+pub const OPTION: [&str; 3] = ["core", "option", "Option"];
+pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
+pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
+pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
+pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"];
+pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
+pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
+pub(super) const PANICKING_PANIC: [&str; 3] = ["core", "panicking", "panic"];
+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_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 PATH: [&str; 3] = ["std", "path", "Path"];
+pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
+pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
+pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
+pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
+pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
+pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
+pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
+pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"];
+pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"];
+pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
+pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
+pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
+pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
+pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
+pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
+pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
+pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"];
+pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
+pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
+pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
+pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
+pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
+pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
+pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
+pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
+pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"];
+pub const RESULT: [&str; 3] = ["core", "result", "Result"];
+pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
+pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
+pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
+pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
+pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
+pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
+pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
+pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"];
+pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
+pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
+pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
+pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
+pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"];
+pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
+pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"];
+pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"];
+pub const STRING: [&str; 3] = ["alloc", "string", "String"];
+pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
+pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
+pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
+pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
+pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
+pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
+#[cfg(feature = "internal-lints")]
+pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
+#[cfg(feature = "internal-lints")]
+pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
+#[cfg(feature = "internal-lints")]
+pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
+#[cfg(feature = "internal-lints")]
+pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
+#[cfg(feature = "internal-lints")]
+pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
+#[cfg(feature = "internal-lints")]
+pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
+pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
+pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
+pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
+pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
+pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
+pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
+pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"];
+pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
+pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
+pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
+pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
+pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
+pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
+pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
+pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
+pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
+pub const WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
diff --git a/src/tools/clippy/clippy_utils/src/ptr.rs b/src/tools/clippy/clippy_utils/src/ptr.rs
new file mode 100644 (file)
index 0000000..baeff08
--- /dev/null
@@ -0,0 +1,86 @@
+use crate::{get_pat_name, match_var, snippet};
+use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::{Body, BodyId, Expr, ExprKind, Param};
+use rustc_lint::LateContext;
+use rustc_middle::hir::map::Map;
+use rustc_span::{Span, Symbol};
+use std::borrow::Cow;
+
+pub fn get_spans(
+    cx: &LateContext<'_>,
+    opt_body_id: Option<BodyId>,
+    idx: usize,
+    replacements: &[(&'static str, &'static str)],
+) -> Option<Vec<(Span, Cow<'static, str>)>> {
+    if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) {
+        get_binding_name(&body.params[idx]).map_or_else(
+            || Some(vec![]),
+            |name| extract_clone_suggestions(cx, name, replacements, body),
+        )
+    } else {
+        Some(vec![])
+    }
+}
+
+fn extract_clone_suggestions<'tcx>(
+    cx: &LateContext<'tcx>,
+    name: Symbol,
+    replace: &[(&'static str, &'static str)],
+    body: &'tcx Body<'_>,
+) -> Option<Vec<(Span, Cow<'static, str>)>> {
+    let mut visitor = PtrCloneVisitor {
+        cx,
+        name,
+        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>,
+    name: Symbol,
+    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;
+        }
+        if let ExprKind::MethodCall(ref seg, _, ref args, _) = expr.kind {
+            if args.len() == 1 && match_var(&args[0], self.name) {
+                if seg.ident.name.as_str() == "capacity" {
+                    self.abort = true;
+                    return;
+                }
+                for &(fn_name, suffix) in self.replace {
+                    if seg.ident.name.as_str() == fn_name {
+                        self.spans
+                            .push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
+                        return;
+                    }
+                }
+            }
+        }
+        walk_expr(self, expr);
+    }
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+}
+
+fn get_binding_name(arg: &Param<'_>) -> Option<Symbol> {
+    get_pat_name(&arg.pat)
+}
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
new file mode 100644 (file)
index 0000000..a482017
--- /dev/null
@@ -0,0 +1,347 @@
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_middle::mir::{
+    Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
+    TerminatorKind,
+};
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
+use rustc_span::symbol::sym;
+use rustc_span::Span;
+use rustc_target::spec::abi::Abi::RustIntrinsic;
+use std::borrow::Cow;
+
+type McfResult = Result<(), (Span, Cow<'static, str>)>;
+
+pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
+    let def_id = body.source.def_id();
+    let mut current = def_id;
+    loop {
+        let predicates = tcx.predicates_of(current);
+        for (predicate, _) in predicates.predicates {
+            match predicate.kind().skip_binder() {
+                ty::PredicateKind::RegionOutlives(_)
+                | ty::PredicateKind::TypeOutlives(_)
+                | ty::PredicateKind::WellFormed(_)
+                | ty::PredicateKind::Projection(_)
+                | ty::PredicateKind::ConstEvaluatable(..)
+                | ty::PredicateKind::ConstEquate(..)
+                | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
+                ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
+                ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
+                ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
+                ty::PredicateKind::Trait(pred, _) => {
+                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
+                        continue;
+                    }
+                    match pred.self_ty().kind() {
+                        ty::Param(ref p) => {
+                            let generics = tcx.generics_of(current);
+                            let def = generics.type_param(p, tcx);
+                            let span = tcx.def_span(def.def_id);
+                            return Err((
+                                span,
+                                "trait bounds other than `Sized` \
+                                 on const fn parameters are unstable"
+                                    .into(),
+                            ));
+                        },
+                        // other kinds of bounds are either tautologies
+                        // or cause errors in other passes
+                        _ => continue,
+                    }
+                },
+            }
+        }
+        match predicates.parent {
+            Some(parent) => current = parent,
+            None => break,
+        }
+    }
+
+    for local in &body.local_decls {
+        check_ty(tcx, local.ty, local.source_info.span)?;
+    }
+    // impl trait is gone in MIR, so check the return type manually
+    check_ty(
+        tcx,
+        tcx.fn_sig(def_id).output().skip_binder(),
+        body.local_decls.iter().next().unwrap().source_info.span,
+    )?;
+
+    for bb in body.basic_blocks() {
+        check_terminator(tcx, body, bb.terminator())?;
+        for stmt in &bb.statements {
+            check_statement(tcx, body, def_id, stmt)?;
+        }
+    }
+    Ok(())
+}
+
+fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
+    for arg in ty.walk() {
+        let ty = match arg.unpack() {
+            GenericArgKind::Type(ty) => ty,
+
+            // No constraints on lifetimes or constants, except potentially
+            // constants' types, but `walk` will get to them as well.
+            GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
+        };
+
+        match ty.kind() {
+            ty::Ref(_, _, hir::Mutability::Mut) => {
+                return Err((span, "mutable references in const fn are unstable".into()));
+            },
+            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
+            ty::FnPtr(..) => {
+                return Err((span, "function pointers in const fn are unstable".into()));
+            },
+            ty::Dynamic(preds, _) => {
+                for pred in preds.iter() {
+                    match pred.skip_binder() {
+                        ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
+                            return Err((
+                                span,
+                                "trait bounds other than `Sized` \
+                                 on const fn parameters are unstable"
+                                    .into(),
+                            ));
+                        },
+                        ty::ExistentialPredicate::Trait(trait_ref) => {
+                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
+                                return Err((
+                                    span,
+                                    "trait bounds other than `Sized` \
+                                     on const fn parameters are unstable"
+                                        .into(),
+                                ));
+                            }
+                        },
+                    }
+                }
+            },
+            _ => {},
+        }
+    }
+    Ok(())
+}
+
+fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
+    match rvalue {
+        Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
+        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
+        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
+            check_place(tcx, *place, span, body)
+        },
+        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
+            use rustc_middle::ty::cast::CastTy;
+            let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
+            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
+            match (cast_in, cast_out) {
+                (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
+                    Err((span, "casting pointers to ints is unstable in const fn".into()))
+                },
+                _ => check_operand(tcx, operand, span, body),
+            }
+        },
+        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
+            check_operand(tcx, operand, span, body)
+        },
+        Rvalue::Cast(
+            CastKind::Pointer(
+                PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
+            ),
+            _,
+            _,
+        ) => Err((span, "function pointer casts are not allowed in const fn".into())),
+        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
+            let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
+                deref_ty.ty
+            } else {
+                // We cannot allow this for now.
+                return Err((span, "unsizing casts are only allowed for references right now".into()));
+            };
+            let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
+            if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
+                check_operand(tcx, op, span, body)?;
+                // Casting/coercing things to slices is fine.
+                Ok(())
+            } else {
+                // We just can't allow trait objects until we have figured out trait method calls.
+                Err((span, "unsizing casts are not allowed in const fn".into()))
+            }
+        },
+        // binops are fine on integers
+        Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
+            check_operand(tcx, lhs, span, body)?;
+            check_operand(tcx, rhs, span, body)?;
+            let ty = lhs.ty(body, tcx);
+            if ty.is_integral() || ty.is_bool() || ty.is_char() {
+                Ok(())
+            } else {
+                Err((
+                    span,
+                    "only int, `bool` and `char` operations are stable in const fn".into(),
+                ))
+            }
+        },
+        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
+        Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
+        Rvalue::UnaryOp(_, operand) => {
+            let ty = operand.ty(body, tcx);
+            if ty.is_integral() || ty.is_bool() {
+                check_operand(tcx, operand, span, body)
+            } else {
+                Err((span, "only int and `bool` operations are stable in const fn".into()))
+            }
+        },
+        Rvalue::Aggregate(_, operands) => {
+            for operand in operands {
+                check_operand(tcx, operand, span, body)?;
+            }
+            Ok(())
+        },
+    }
+}
+
+fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
+    let span = statement.source_info.span;
+    match &statement.kind {
+        StatementKind::Assign(box (place, rval)) => {
+            check_place(tcx, *place, span, body)?;
+            check_rvalue(tcx, body, def_id, rval, span)
+        },
+
+        StatementKind::FakeRead(_, place) |
+        // just an assignment
+        StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),
+
+        StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
+
+        // These are all NOPs
+        StatementKind::StorageLive(_)
+        | StatementKind::StorageDead(_)
+        | StatementKind::Retag { .. }
+        | StatementKind::AscribeUserType(..)
+        | StatementKind::Coverage(..)
+        | StatementKind::Nop => Ok(()),
+    }
+}
+
+fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
+    match operand {
+        Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
+        Operand::Constant(c) => match c.check_static_ptr(tcx) {
+            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
+            None => Ok(()),
+        },
+    }
+}
+
+fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
+    let mut cursor = place.projection.as_ref();
+    while let [ref proj_base @ .., elem] = *cursor {
+        cursor = proj_base;
+        match elem {
+            ProjectionElem::Field(..) => {
+                let base_ty = Place::ty_from(place.local, &proj_base, body, tcx).ty;
+                if let Some(def) = base_ty.ty_adt_def() {
+                    // No union field accesses in `const fn`
+                    if def.is_union() {
+                        return Err((span, "accessing union fields is unstable".into()));
+                    }
+                }
+            },
+            ProjectionElem::ConstantIndex { .. }
+            | ProjectionElem::Downcast(..)
+            | ProjectionElem::Subslice { .. }
+            | ProjectionElem::Deref
+            | ProjectionElem::Index(_) => {},
+        }
+    }
+
+    Ok(())
+}
+
+fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
+    let span = terminator.source_info.span;
+    match &terminator.kind {
+        TerminatorKind::FalseEdge { .. }
+        | TerminatorKind::FalseUnwind { .. }
+        | TerminatorKind::Goto { .. }
+        | TerminatorKind::Return
+        | TerminatorKind::Resume
+        | TerminatorKind::Unreachable => Ok(()),
+
+        TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
+        TerminatorKind::DropAndReplace { place, value, .. } => {
+            check_place(tcx, *place, span, body)?;
+            check_operand(tcx, value, span, body)
+        },
+
+        TerminatorKind::SwitchInt {
+            discr,
+            switch_ty: _,
+            targets: _,
+        } => check_operand(tcx, discr, span, body),
+
+        TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
+        TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
+            Err((span, "const fn generators are unstable".into()))
+        },
+
+        TerminatorKind::Call {
+            func,
+            args,
+            from_hir_call: _,
+            destination: _,
+            cleanup: _,
+            fn_span: _,
+        } => {
+            let fn_ty = func.ty(body, tcx);
+            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
+                if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
+                    return Err((
+                        span,
+                        format!(
+                            "can only call other `const fn` within a `const fn`, \
+                             but `{:?}` is not stable as `const fn`",
+                            func,
+                        )
+                        .into(),
+                    ));
+                }
+
+                // HACK: This is to "unstabilize" the `transmute` intrinsic
+                // within const fns. `transmute` is allowed in all other const contexts.
+                // This won't really scale to more intrinsics or functions. Let's allow const
+                // transmutes in const fn before we add more hacks to this.
+                if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
+                    return Err((
+                        span,
+                        "can only call `transmute` from const items, not `const fn`".into(),
+                    ));
+                }
+
+                check_operand(tcx, func, span, body)?;
+
+                for arg in args {
+                    check_operand(tcx, arg, span, body)?;
+                }
+                Ok(())
+            } else {
+                Err((span, "can only call other const fns within const fn".into()))
+            }
+        },
+
+        TerminatorKind::Assert {
+            cond,
+            expected: _,
+            msg: _,
+            target: _,
+            cleanup: _,
+        } => check_operand(tcx, cond, span, body),
+
+        TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
new file mode 100644 (file)
index 0000000..d4f6f42
--- /dev/null
@@ -0,0 +1,683 @@
+//! Contains utility functions to generate suggestions.
+#![deny(clippy::missing_docs_in_private_items)]
+
+use crate::{higher, snippet, snippet_opt, snippet_with_macro_callsite};
+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_lint::{EarlyContext, LateContext, LintContext};
+use rustc_span::source_map::{CharPos, Span};
+use rustc_span::{BytePos, Pos};
+use std::borrow::Cow;
+use std::convert::TryInto;
+use std::fmt::Display;
+use std::ops::{Add, Neg, Not, Sub};
+
+/// A helper type to build suggestion correctly handling parenthesis.
+#[derive(Clone, PartialEq)]
+pub enum Sugg<'a> {
+    /// An expression that never needs parenthesis such as `1337` or `[0; 42]`.
+    NonParen(Cow<'a, str>),
+    /// An expression that does not fit in other variants.
+    MaybeParen(Cow<'a, str>),
+    /// A binary operator expression, including `as`-casts and explicit type
+    /// coercion.
+    BinOp(AssocOp, Cow<'a, str>),
+}
+
+/// Literal constant `0`, for convenience.
+pub const ZERO: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("0"));
+/// Literal constant `1`, for convenience.
+pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
+/// a constant represents an empty string, for convenience.
+pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed(""));
+
+impl Display for Sugg<'_> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+        match *self {
+            Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => s.fmt(f),
+        }
+    }
+}
+
+#[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method
+impl<'a> Sugg<'a> {
+    /// Prepare a suggestion from an expression.
+    pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Self> {
+        snippet_opt(cx, expr.span).map(|snippet| {
+            let snippet = Cow::Owned(snippet);
+            Self::hir_from_snippet(expr, snippet)
+        })
+    }
+
+    /// Convenience function around `hir_opt` for suggestions with a default
+    /// text.
+    pub fn hir(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
+        Self::hir_opt(cx, expr).unwrap_or(Sugg::NonParen(Cow::Borrowed(default)))
+    }
+
+    /// Same as `hir`, but it adapts the applicability level by following rules:
+    ///
+    /// - Applicability level `Unspecified` will never be changed.
+    /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
+    /// - If the default value is used and the applicability level is `MachineApplicable`, change it
+    ///   to
+    /// `HasPlaceholders`
+    pub fn hir_with_applicability(
+        cx: &LateContext<'_>,
+        expr: &hir::Expr<'_>,
+        default: &'a str,
+        applicability: &mut Applicability,
+    ) -> Self {
+        if *applicability != Applicability::Unspecified && expr.span.from_expansion() {
+            *applicability = Applicability::MaybeIncorrect;
+        }
+        Self::hir_opt(cx, expr).unwrap_or_else(|| {
+            if *applicability == Applicability::MachineApplicable {
+                *applicability = Applicability::HasPlaceholders;
+            }
+            Sugg::NonParen(Cow::Borrowed(default))
+        })
+    }
+
+    /// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro.
+    pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
+        let snippet = snippet_with_macro_callsite(cx, expr.span, default);
+
+        Self::hir_from_snippet(expr, snippet)
+    }
+
+    /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
+    /// function variants of `Sugg`, since these use different snippet functions.
+    fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self {
+        if let Some(range) = higher::range(expr) {
+            let op = match range.limits {
+                ast::RangeLimits::HalfOpen => AssocOp::DotDot,
+                ast::RangeLimits::Closed => AssocOp::DotDotEq,
+            };
+            return Sugg::BinOp(op, snippet);
+        }
+
+        match expr.kind {
+            hir::ExprKind::AddrOf(..)
+            | hir::ExprKind::Box(..)
+            | hir::ExprKind::If(..)
+            | hir::ExprKind::Closure(..)
+            | hir::ExprKind::Unary(..)
+            | hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
+            hir::ExprKind::Continue(..)
+            | hir::ExprKind::Yield(..)
+            | hir::ExprKind::Array(..)
+            | hir::ExprKind::Block(..)
+            | hir::ExprKind::Break(..)
+            | hir::ExprKind::Call(..)
+            | hir::ExprKind::Field(..)
+            | hir::ExprKind::Index(..)
+            | hir::ExprKind::InlineAsm(..)
+            | hir::ExprKind::LlvmInlineAsm(..)
+            | hir::ExprKind::ConstBlock(..)
+            | hir::ExprKind::Lit(..)
+            | hir::ExprKind::Loop(..)
+            | hir::ExprKind::MethodCall(..)
+            | hir::ExprKind::Path(..)
+            | hir::ExprKind::Repeat(..)
+            | hir::ExprKind::Ret(..)
+            | hir::ExprKind::Struct(..)
+            | hir::ExprKind::Tup(..)
+            | hir::ExprKind::DropTemps(_)
+            | hir::ExprKind::Err => Sugg::NonParen(snippet),
+            hir::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
+            hir::ExprKind::AssignOp(op, ..) => Sugg::BinOp(hirbinop2assignop(op), snippet),
+            hir::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(higher::binop(op.node)), snippet),
+            hir::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
+            hir::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
+        }
+    }
+
+    /// Prepare a suggestion from an expression.
+    pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
+        use rustc_ast::ast::RangeLimits;
+
+        let snippet = if expr.span.from_expansion() {
+            snippet_with_macro_callsite(cx, expr.span, default)
+        } else {
+            snippet(cx, expr.span, default)
+        };
+
+        match expr.kind {
+            ast::ExprKind::AddrOf(..)
+            | ast::ExprKind::Box(..)
+            | ast::ExprKind::Closure(..)
+            | ast::ExprKind::If(..)
+            | ast::ExprKind::Let(..)
+            | ast::ExprKind::Unary(..)
+            | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
+            ast::ExprKind::Async(..)
+            | ast::ExprKind::Block(..)
+            | ast::ExprKind::Break(..)
+            | ast::ExprKind::Call(..)
+            | ast::ExprKind::Continue(..)
+            | ast::ExprKind::Yield(..)
+            | ast::ExprKind::Field(..)
+            | ast::ExprKind::ForLoop(..)
+            | ast::ExprKind::Index(..)
+            | ast::ExprKind::InlineAsm(..)
+            | ast::ExprKind::LlvmInlineAsm(..)
+            | ast::ExprKind::ConstBlock(..)
+            | ast::ExprKind::Lit(..)
+            | ast::ExprKind::Loop(..)
+            | ast::ExprKind::MacCall(..)
+            | ast::ExprKind::MethodCall(..)
+            | ast::ExprKind::Paren(..)
+            | ast::ExprKind::Underscore
+            | ast::ExprKind::Path(..)
+            | ast::ExprKind::Repeat(..)
+            | ast::ExprKind::Ret(..)
+            | ast::ExprKind::Struct(..)
+            | ast::ExprKind::Try(..)
+            | ast::ExprKind::TryBlock(..)
+            | ast::ExprKind::Tup(..)
+            | ast::ExprKind::Array(..)
+            | ast::ExprKind::While(..)
+            | ast::ExprKind::Await(..)
+            | ast::ExprKind::Err => Sugg::NonParen(snippet),
+            ast::ExprKind::Range(.., RangeLimits::HalfOpen) => Sugg::BinOp(AssocOp::DotDot, snippet),
+            ast::ExprKind::Range(.., RangeLimits::Closed) => Sugg::BinOp(AssocOp::DotDotEq, snippet),
+            ast::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
+            ast::ExprKind::AssignOp(op, ..) => Sugg::BinOp(astbinop2assignop(op), snippet),
+            ast::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node), snippet),
+            ast::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
+            ast::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
+        }
+    }
+
+    /// Convenience method to create the `<lhs> && <rhs>` suggestion.
+    pub fn and(self, rhs: &Self) -> Sugg<'static> {
+        make_binop(ast::BinOpKind::And, &self, rhs)
+    }
+
+    /// Convenience method to create the `<lhs> & <rhs>` suggestion.
+    pub fn bit_and(self, rhs: &Self) -> Sugg<'static> {
+        make_binop(ast::BinOpKind::BitAnd, &self, rhs)
+    }
+
+    /// Convenience method to create the `<lhs> as <rhs>` suggestion.
+    pub fn as_ty<R: Display>(self, rhs: R) -> Sugg<'static> {
+        make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into()))
+    }
+
+    /// Convenience method to create the `&<expr>` suggestion.
+    pub fn addr(self) -> Sugg<'static> {
+        make_unop("&", self)
+    }
+
+    /// Convenience method to create the `&mut <expr>` suggestion.
+    pub fn mut_addr(self) -> Sugg<'static> {
+        make_unop("&mut ", self)
+    }
+
+    /// Convenience method to create the `*<expr>` suggestion.
+    pub fn deref(self) -> Sugg<'static> {
+        make_unop("*", self)
+    }
+
+    /// Convenience method to create the `&*<expr>` suggestion. Currently this
+    /// is needed because `sugg.deref().addr()` produces an unnecessary set of
+    /// parentheses around the deref.
+    pub fn addr_deref(self) -> Sugg<'static> {
+        make_unop("&*", self)
+    }
+
+    /// Convenience method to create the `&mut *<expr>` suggestion. Currently
+    /// this is needed because `sugg.deref().mut_addr()` produces an unnecessary
+    /// set of parentheses around the deref.
+    pub fn mut_addr_deref(self) -> Sugg<'static> {
+        make_unop("&mut *", self)
+    }
+
+    /// Convenience method to transform suggestion into a return call
+    pub fn make_return(self) -> Sugg<'static> {
+        Sugg::NonParen(Cow::Owned(format!("return {}", self)))
+    }
+
+    /// Convenience method to transform suggestion into a block
+    /// where the suggestion is a trailing expression
+    pub fn blockify(self) -> Sugg<'static> {
+        Sugg::NonParen(Cow::Owned(format!("{{ {} }}", self)))
+    }
+
+    /// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
+    /// suggestion.
+    #[allow(dead_code)]
+    pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> {
+        match limit {
+            ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end),
+            ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, end),
+        }
+    }
+
+    /// Adds parenthesis to any expression that might need them. Suitable to the
+    /// `self` argument of a method call
+    /// (e.g., to build `bar.foo()` or `(1 + 2).foo()`).
+    pub fn maybe_par(self) -> Self {
+        match self {
+            Sugg::NonParen(..) => self,
+            // `(x)` and `(x).y()` both don't need additional parens.
+            Sugg::MaybeParen(sugg) => {
+                if sugg.starts_with('(') && sugg.ends_with(')') {
+                    Sugg::MaybeParen(sugg)
+                } else {
+                    Sugg::NonParen(format!("({})", sugg).into())
+                }
+            },
+            Sugg::BinOp(_, sugg) => Sugg::NonParen(format!("({})", sugg).into()),
+        }
+    }
+}
+
+// Copied from the rust standart library, and then edited
+macro_rules! forward_binop_impls_to_ref {
+    (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => {
+        impl $imp<$t> for &$t {
+            type Output = $o;
+
+            fn $method(self, other: $t) -> $o {
+                $imp::$method(self, &other)
+            }
+        }
+
+        impl $imp<&$t> for $t {
+            type Output = $o;
+
+            fn $method(self, other: &$t) -> $o {
+                $imp::$method(&self, other)
+            }
+        }
+
+        impl $imp for $t {
+            type Output = $o;
+
+            fn $method(self, other: $t) -> $o {
+                $imp::$method(&self, &other)
+            }
+        }
+    };
+}
+
+impl Add for &Sugg<'_> {
+    type Output = Sugg<'static>;
+    fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> {
+        make_binop(ast::BinOpKind::Add, self, rhs)
+    }
+}
+
+impl Sub for &Sugg<'_> {
+    type Output = Sugg<'static>;
+    fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> {
+        make_binop(ast::BinOpKind::Sub, self, rhs)
+    }
+}
+
+forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>);
+forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>);
+
+impl Neg for Sugg<'_> {
+    type Output = Sugg<'static>;
+    fn neg(self) -> Sugg<'static> {
+        make_unop("-", self)
+    }
+}
+
+impl Not for Sugg<'_> {
+    type Output = Sugg<'static>;
+    fn not(self) -> Sugg<'static> {
+        make_unop("!", self)
+    }
+}
+
+/// Helper type to display either `foo` or `(foo)`.
+struct ParenHelper<T> {
+    /// `true` if parentheses are needed.
+    paren: bool,
+    /// The main thing to display.
+    wrapped: T,
+}
+
+impl<T> ParenHelper<T> {
+    /// Builds a `ParenHelper`.
+    fn new(paren: bool, wrapped: T) -> Self {
+        Self { paren, wrapped }
+    }
+}
+
+impl<T: Display> Display for ParenHelper<T> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+        if self.paren {
+            write!(f, "({})", self.wrapped)
+        } else {
+            self.wrapped.fmt(f)
+        }
+    }
+}
+
+/// Builds the string for `<op><expr>` adding parenthesis when necessary.
+///
+/// For convenience, the operator is taken as a string because all unary
+/// operators have the same
+/// precedence.
+pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
+    Sugg::MaybeParen(format!("{}{}", op, expr.maybe_par()).into())
+}
+
+/// Builds the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
+///
+/// Precedence of shift operator relative to other arithmetic operation is
+/// often confusing so
+/// parenthesis will always be added for a mix of these.
+pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
+    /// Returns `true` if the operator is a shift operator `<<` or `>>`.
+    fn is_shift(op: AssocOp) -> bool {
+        matches!(op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
+    }
+
+    /// Returns `true` if the operator is a arithmetic operator
+    /// (i.e., `+`, `-`, `*`, `/`, `%`).
+    fn is_arith(op: AssocOp) -> bool {
+        matches!(
+            op,
+            AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus
+        )
+    }
+
+    /// Returns `true` if the operator `op` needs parenthesis with the operator
+    /// `other` in the direction `dir`.
+    fn needs_paren(op: AssocOp, other: AssocOp, dir: Associativity) -> bool {
+        other.precedence() < op.precedence()
+            || (other.precedence() == op.precedence()
+                && ((op != other && associativity(op) != dir)
+                    || (op == other && associativity(op) != Associativity::Both)))
+            || is_shift(op) && is_arith(other)
+            || is_shift(other) && is_arith(op)
+    }
+
+    let lhs_paren = if let Sugg::BinOp(lop, _) = *lhs {
+        needs_paren(op, lop, Associativity::Left)
+    } else {
+        false
+    };
+
+    let rhs_paren = if let Sugg::BinOp(rop, _) = *rhs {
+        needs_paren(op, rop, Associativity::Right)
+    } else {
+        false
+    };
+
+    let lhs = ParenHelper::new(lhs_paren, lhs);
+    let rhs = ParenHelper::new(rhs_paren, rhs);
+    let sugg = match op {
+        AssocOp::Add
+        | AssocOp::BitAnd
+        | AssocOp::BitOr
+        | AssocOp::BitXor
+        | AssocOp::Divide
+        | AssocOp::Equal
+        | AssocOp::Greater
+        | AssocOp::GreaterEqual
+        | AssocOp::LAnd
+        | AssocOp::LOr
+        | AssocOp::Less
+        | AssocOp::LessEqual
+        | AssocOp::Modulus
+        | AssocOp::Multiply
+        | AssocOp::NotEqual
+        | AssocOp::ShiftLeft
+        | AssocOp::ShiftRight
+        | AssocOp::Subtract => format!(
+            "{} {} {}",
+            lhs,
+            op.to_ast_binop().expect("Those are AST ops").to_string(),
+            rhs
+        ),
+        AssocOp::Assign => format!("{} = {}", lhs, rhs),
+        AssocOp::AssignOp(op) => format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs),
+        AssocOp::As => format!("{} as {}", lhs, rhs),
+        AssocOp::DotDot => format!("{}..{}", lhs, rhs),
+        AssocOp::DotDotEq => format!("{}..={}", lhs, rhs),
+        AssocOp::Colon => format!("{}: {}", lhs, rhs),
+    };
+
+    Sugg::BinOp(op, sugg.into())
+}
+
+/// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`.
+pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
+    make_assoc(AssocOp::from_ast_binop(op), lhs, rhs)
+}
+
+#[derive(PartialEq, Eq, Clone, Copy)]
+/// Operator associativity.
+enum Associativity {
+    /// The operator is both left-associative and right-associative.
+    Both,
+    /// The operator is left-associative.
+    Left,
+    /// The operator is not associative.
+    None,
+    /// The operator is right-associative.
+    Right,
+}
+
+/// Returns the associativity/fixity of an operator. The difference with
+/// `AssocOp::fixity` is that an operator can be both left and right associative
+/// (such as `+`: `a + b + c == (a + b) + c == a + (b + c)`.
+///
+/// Chained `as` and explicit `:` type coercion never need inner parenthesis so
+/// they are considered
+/// associative.
+#[must_use]
+fn associativity(op: AssocOp) -> Associativity {
+    use rustc_ast::util::parser::AssocOp::{
+        Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Colon, Divide, DotDot, DotDotEq, Equal, Greater,
+        GreaterEqual, LAnd, LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract,
+    };
+
+    match op {
+        Assign | AssignOp(_) => Associativity::Right,
+        Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both,
+        Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight
+        | Subtract => Associativity::Left,
+        DotDot | DotDotEq => Associativity::None,
+    }
+}
+
+/// Converts a `hir::BinOp` to the corresponding assigning binary operator.
+fn hirbinop2assignop(op: hir::BinOp) -> AssocOp {
+    use rustc_ast::token::BinOpToken::{And, Caret, Minus, Or, Percent, Plus, Shl, Shr, Slash, Star};
+
+    AssocOp::AssignOp(match op.node {
+        hir::BinOpKind::Add => Plus,
+        hir::BinOpKind::BitAnd => And,
+        hir::BinOpKind::BitOr => Or,
+        hir::BinOpKind::BitXor => Caret,
+        hir::BinOpKind::Div => Slash,
+        hir::BinOpKind::Mul => Star,
+        hir::BinOpKind::Rem => Percent,
+        hir::BinOpKind::Shl => Shl,
+        hir::BinOpKind::Shr => Shr,
+        hir::BinOpKind::Sub => Minus,
+
+        hir::BinOpKind::And
+        | hir::BinOpKind::Eq
+        | hir::BinOpKind::Ge
+        | hir::BinOpKind::Gt
+        | hir::BinOpKind::Le
+        | hir::BinOpKind::Lt
+        | hir::BinOpKind::Ne
+        | hir::BinOpKind::Or => panic!("This operator does not exist"),
+    })
+}
+
+/// Converts an `ast::BinOp` to the corresponding assigning binary operator.
+fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
+    use rustc_ast::ast::BinOpKind::{
+        Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
+    };
+    use rustc_ast::token::BinOpToken;
+
+    AssocOp::AssignOp(match op.node {
+        Add => BinOpToken::Plus,
+        BitAnd => BinOpToken::And,
+        BitOr => BinOpToken::Or,
+        BitXor => BinOpToken::Caret,
+        Div => BinOpToken::Slash,
+        Mul => BinOpToken::Star,
+        Rem => BinOpToken::Percent,
+        Shl => BinOpToken::Shl,
+        Shr => BinOpToken::Shr,
+        Sub => BinOpToken::Minus,
+        And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"),
+    })
+}
+
+/// Returns the indentation before `span` if there are nothing but `[ \t]`
+/// before it on its line.
+fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
+    let lo = cx.sess().source_map().lookup_char_pos(span.lo());
+    lo.file
+        .get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */)
+        .and_then(|line| {
+            if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
+                // We can mix char and byte positions here because we only consider `[ \t]`.
+                if lo.col == CharPos(pos) {
+                    Some(line[..pos].into())
+                } else {
+                    None
+                }
+            } else {
+                None
+            }
+        })
+}
+
+/// Convenience extension trait for `DiagnosticBuilder`.
+pub trait DiagnosticBuilderExt<T: LintContext> {
+    /// Suggests to add an attribute to an item.
+    ///
+    /// Correctly handles indentation of the attribute and item.
+    ///
+    /// # Example
+    ///
+    /// ```rust,ignore
+    /// diag.suggest_item_with_attr(cx, item, "#[derive(Default)]");
+    /// ```
+    fn suggest_item_with_attr<D: Display + ?Sized>(
+        &mut self,
+        cx: &T,
+        item: Span,
+        msg: &str,
+        attr: &D,
+        applicability: Applicability,
+    );
+
+    /// Suggest to add an item before another.
+    ///
+    /// The item should not be indented (except for inner indentation).
+    ///
+    /// # Example
+    ///
+    /// ```rust,ignore
+    /// diag.suggest_prepend_item(cx, item,
+    /// "fn foo() {
+    ///     bar();
+    /// }");
+    /// ```
+    fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability);
+
+    /// Suggest to completely remove an item.
+    ///
+    /// This will remove an item and all following whitespace until the next non-whitespace
+    /// character. This should work correctly if item is on the same indentation level as the
+    /// following item.
+    ///
+    /// # Example
+    ///
+    /// ```rust,ignore
+    /// diag.suggest_remove_item(cx, item, "remove this")
+    /// ```
+    fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability);
+}
+
+impl<T: LintContext> DiagnosticBuilderExt<T> for rustc_errors::DiagnosticBuilder<'_> {
+    fn suggest_item_with_attr<D: Display + ?Sized>(
+        &mut self,
+        cx: &T,
+        item: Span,
+        msg: &str,
+        attr: &D,
+        applicability: Applicability,
+    ) {
+        if let Some(indent) = indentation(cx, item) {
+            let span = item.with_hi(item.lo());
+
+            self.span_suggestion(span, msg, format!("{}\n{}", attr, indent), applicability);
+        }
+    }
+
+    fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability) {
+        if let Some(indent) = indentation(cx, item) {
+            let span = item.with_hi(item.lo());
+
+            let mut first = true;
+            let new_item = new_item
+                .lines()
+                .map(|l| {
+                    if first {
+                        first = false;
+                        format!("{}\n", l)
+                    } else {
+                        format!("{}{}\n", indent, l)
+                    }
+                })
+                .collect::<String>();
+
+            self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent), applicability);
+        }
+    }
+
+    fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) {
+        let mut remove_span = item;
+        let hi = cx.sess().source_map().next_point(remove_span).hi();
+        let fmpos = cx.sess().source_map().lookup_byte_offset(hi);
+
+        if let Some(ref src) = fmpos.sf.src {
+            let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n');
+
+            if let Some(non_whitespace_offset) = non_whitespace_offset {
+                remove_span = remove_span
+                    .with_hi(remove_span.hi() + BytePos(non_whitespace_offset.try_into().expect("offset too large")))
+            }
+        }
+
+        self.span_suggestion(remove_span, msg, String::new(), applicability);
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::Sugg;
+    use std::borrow::Cow;
+
+    const SUGGESTION: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("function_call()"));
+
+    #[test]
+    fn make_return_transform_sugg_into_a_return_call() {
+        assert_eq!("return function_call()", SUGGESTION.make_return().to_string());
+    }
+
+    #[test]
+    fn blockify_transforms_sugg_into_a_block() {
+        assert_eq!("{ function_call() }", SUGGESTION.blockify().to_string());
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/sym_helper.rs b/src/tools/clippy/clippy_utils/src/sym_helper.rs
new file mode 100644 (file)
index 0000000..f47dc80
--- /dev/null
@@ -0,0 +1,7 @@
+#[macro_export]
+/// Convenience wrapper around rustc's `Symbol::intern`
+macro_rules! sym {
+    ($tt:tt) => {
+        rustc_span::symbol::Symbol::intern(stringify!($tt))
+    };
+}
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
new file mode 100644 (file)
index 0000000..d577827
--- /dev/null
@@ -0,0 +1,198 @@
+use crate as utils;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir as hir;
+use rustc_hir::def::Res;
+use rustc_hir::intravisit;
+use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
+use rustc_hir::{Expr, ExprKind, HirId, Path};
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_lint::LateContext;
+use rustc_middle::hir::map::Map;
+use rustc_middle::ty;
+use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
+
+/// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined.
+pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option<FxHashSet<HirId>> {
+    let mut delegate = MutVarsDelegate {
+        used_mutably: FxHashSet::default(),
+        skip: false,
+    };
+    cx.tcx.infer_ctxt().enter(|infcx| {
+        ExprUseVisitor::new(
+            &mut delegate,
+            &infcx,
+            expr.hir_id.owner,
+            cx.param_env,
+            cx.typeck_results(),
+        )
+        .walk_expr(expr);
+    });
+
+    if delegate.skip {
+        return None;
+    }
+    Some(delegate.used_mutably)
+}
+
+pub fn is_potentially_mutated<'tcx>(variable: &'tcx Path<'_>, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
+    if let Res::Local(id) = variable.res {
+        mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&id))
+    } else {
+        true
+    }
+}
+
+struct MutVarsDelegate {
+    used_mutably: FxHashSet<HirId>,
+    skip: bool,
+}
+
+impl<'tcx> MutVarsDelegate {
+    #[allow(clippy::similar_names)]
+    fn update(&mut self, cat: &PlaceWithHirId<'tcx>) {
+        match cat.place.base {
+            PlaceBase::Local(id) => {
+                self.used_mutably.insert(id);
+            },
+            PlaceBase::Upvar(_) => {
+                //FIXME: This causes false negatives. We can't get the `NodeId` from
+                //`Categorization::Upvar(_)`. So we search for any `Upvar`s in the
+                //`while`-body, not just the ones in the condition.
+                self.skip = true
+            },
+            _ => {},
+        }
+    }
+}
+
+impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
+    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ConsumeMode) {}
+
+    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, bk: ty::BorrowKind) {
+        if let ty::BorrowKind::MutBorrow = bk {
+            self.update(&cmt)
+        }
+    }
+
+    fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
+        self.update(&cmt)
+    }
+}
+
+pub struct ParamBindingIdCollector {
+    binding_hir_ids: Vec<hir::HirId>,
+}
+impl<'tcx> ParamBindingIdCollector {
+    fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
+        let mut hir_ids: Vec<hir::HirId> = Vec::new();
+        for param in body.params.iter() {
+            let mut finder = ParamBindingIdCollector {
+                binding_hir_ids: Vec::new(),
+            };
+            finder.visit_param(param);
+            for hir_id in &finder.binding_hir_ids {
+                hir_ids.push(*hir_id);
+            }
+        }
+        hir_ids
+    }
+}
+impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector {
+    type Map = Map<'tcx>;
+
+    fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
+        if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind {
+            self.binding_hir_ids.push(hir_id);
+        }
+        intravisit::walk_pat(self, pat);
+    }
+
+    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+        intravisit::NestedVisitorMap::None
+    }
+}
+
+pub struct BindingUsageFinder<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    binding_ids: Vec<hir::HirId>,
+    usage_found: bool,
+}
+impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> {
+    pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool {
+        let mut finder = BindingUsageFinder {
+            cx,
+            binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body),
+            usage_found: false,
+        };
+        finder.visit_body(body);
+        finder.usage_found
+    }
+}
+impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
+    type Map = Map<'tcx>;
+
+    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+        if !self.usage_found {
+            intravisit::walk_expr(self, expr);
+        }
+    }
+
+    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+        if let hir::def::Res::Local(id) = path.res {
+            if self.binding_ids.contains(&id) {
+                self.usage_found = true;
+            }
+        }
+    }
+
+    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+        intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+    }
+}
+
+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;
+        }
+        match &ex.kind {
+            ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
+                self.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);
+                }
+            },
+        }
+    }
+}
+
+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
+}
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
new file mode 100644 (file)
index 0000000..5a8c629
--- /dev/null
@@ -0,0 +1,190 @@
+use crate::path_to_local_id;
+use rustc_hir as hir;
+use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::{Arm, Body, Expr, HirId, Stmt};
+use rustc_lint::LateContext;
+use rustc_middle::hir::map::Map;
+
+/// returns `true` if expr contains match expr desugared from try
+fn contains_try(expr: &hir::Expr<'_>) -> bool {
+    struct TryFinder {
+        found: bool,
+    }
+
+    impl<'hir> intravisit::Visitor<'hir> for TryFinder {
+        type Map = Map<'hir>;
+
+        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+            intravisit::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),
+            }
+        }
+    }
+
+    let mut visitor = TryFinder { found: false };
+    visitor.visit_expr(expr);
+    visitor.found
+}
+
+pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
+where
+    F: FnMut(&'hir hir::Expr<'hir>) -> bool,
+{
+    struct RetFinder<F> {
+        in_stmt: bool,
+        failed: bool,
+        cb: F,
+    }
+
+    struct WithStmtGuarg<'a, F> {
+        val: &'a mut RetFinder<F>,
+        prev_in_stmt: bool,
+    }
+
+    impl<F> RetFinder<F> {
+        fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
+            let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
+            WithStmtGuarg {
+                val: self,
+                prev_in_stmt,
+            }
+        }
+    }
+
+    impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
+        type Target = RetFinder<F>;
+
+        fn deref(&self) -> &Self::Target {
+            self.val
+        }
+    }
+
+    impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            self.val
+        }
+    }
+
+    impl<F> Drop for WithStmtGuarg<'_, F> {
+        fn drop(&mut self) {
+            self.val.in_stmt = self.prev_in_stmt;
+        }
+    }
+
+    impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
+        type Map = Map<'hir>;
+
+        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+            intravisit::NestedVisitorMap::None
+        }
+
+        fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) {
+            intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt)
+        }
+
+        fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
+            if self.failed {
+                return;
+            }
+            if self.in_stmt {
+                match expr.kind {
+                    hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
+                    _ => intravisit::walk_expr(self, expr),
+                }
+            } else {
+                match expr.kind {
+                    hir::ExprKind::If(cond, then, else_opt) => {
+                        self.inside_stmt(true).visit_expr(cond);
+                        self.visit_expr(then);
+                        if let Some(el) = else_opt {
+                            self.visit_expr(el);
+                        }
+                    },
+                    hir::ExprKind::Match(cond, arms, _) => {
+                        self.inside_stmt(true).visit_expr(cond);
+                        for arm in arms {
+                            self.visit_expr(arm.body);
+                        }
+                    },
+                    hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr),
+                    hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
+                    _ => self.failed |= !(self.cb)(expr),
+                }
+            }
+        }
+    }
+
+    !contains_try(expr) && {
+        let mut ret_finder = RetFinder {
+            in_stmt: false,
+            failed: false,
+            cb: callback,
+        };
+        ret_finder.visit_expr(expr);
+        !ret_finder.failed
+    }
+}
+
+pub struct LocalUsedVisitor<'hir> {
+    hir: Map<'hir>,
+    pub local_hir_id: HirId,
+    pub used: bool,
+}
+
+impl<'hir> LocalUsedVisitor<'hir> {
+    pub fn new(cx: &LateContext<'hir>, local_hir_id: HirId) -> Self {
+        Self {
+            hir: cx.tcx.hir(),
+            local_hir_id,
+            used: false,
+        }
+    }
+
+    fn check<T>(&mut self, t: T, visit: fn(&mut Self, T)) -> bool {
+        visit(self, t);
+        std::mem::replace(&mut self.used, false)
+    }
+
+    pub fn check_arm(&mut self, arm: &'hir Arm<'_>) -> bool {
+        self.check(arm, Self::visit_arm)
+    }
+
+    pub fn check_body(&mut self, body: &'hir Body<'_>) -> bool {
+        self.check(body, Self::visit_body)
+    }
+
+    pub fn check_expr(&mut self, expr: &'hir Expr<'_>) -> bool {
+        self.check(expr, Self::visit_expr)
+    }
+
+    pub fn check_stmt(&mut self, stmt: &'hir Stmt<'_>) -> bool {
+        self.check(stmt, Self::visit_stmt)
+    }
+}
+
+impl<'v> Visitor<'v> for LocalUsedVisitor<'v> {
+    type Map = Map<'v>;
+
+    fn visit_expr(&mut self, expr: &'v Expr<'v>) {
+        if self.used {
+            return;
+        }
+        if path_to_local_id(expr, self.local_hir_id) {
+            self.used = true;
+        } else {
+            walk_expr(self, expr);
+        }
+    }
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::OnlyBodies(self.hir)
+    }
+}
index 8fd1dea9aeef9bfe6843c7df2c35e0d45e49ec67..f62c2d29c707e3e45eb143bdc0fc4895021eebf0 100644 (file)
@@ -108,6 +108,9 @@ should only commit files changed by `cargo dev bless` for the
 specific lint you are creating/editing. Note that if the generated files are
 empty, they should be removed.
 
+Note that you can run multiple test files by specifying a comma separated list:
+`TESTNAME=foo_functions,test2,test3`.
+
 ### Cargo lints
 
 For cargo lints, the process of testing differs in that we are interested in
@@ -289,7 +292,7 @@ the next section. Let's worry about the details later and emit our lint for
 
 Depending on how complex we want our lint message to be, we can choose from a
 variety of lint emission functions. They can all be found in
-[`clippy_lints/src/utils/diagnostics.rs`][diagnostics].
+[`clippy_utils/src/diagnostics.rs`][diagnostics].
 
 `span_lint_and_help` seems most appropriate in this case. It allows us to
 provide an extra help message and we can't really suggest a better name
@@ -318,7 +321,7 @@ When code or an identifier must appear in a message or label, it should be
 surrounded with single grave accents \`.
 
 [check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
-[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs
+[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/diagnostics.rs
 [the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html
 
 ## Adding the lint logic
@@ -534,7 +537,7 @@ directory. Adding a configuration to a lint can be useful for thresholds or to c
 behavior that can be seen as a false positive for some users. Adding a configuration is done 
 in the following steps:
 
-1. Adding a new configuration entry to [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs)
+1. Adding a new configuration entry to [clippy_utils::conf](/clippy_utils/src/conf.rs)
     like this:
     ```rust
     /// Lint: LINT_NAME. <The configuration field doc comment>
@@ -633,7 +636,7 @@ 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_lints/src/utils/mod.rs
+[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
 [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 d56079a4ab735f4eab65e36de5ae65270f3c7864..abac1227b4ff389cee02b348ddf01ad9a5346b78 100644 (file)
@@ -78,7 +78,7 @@ impl LateLintPass<'_> for MyStructLint {
 There are two ways to do this, depending if the target trait is part of lang items.
 
 ```rust
-use crate::utils::{implements_trait, match_trait_method, paths};
+use clippy_utils::{implements_trait, match_trait_method, paths};
 
 impl LateLintPass<'_> for MyStructLint {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
@@ -112,7 +112,7 @@ We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`]
 To check if our type defines a method called `some_method`:
 
 ```rust
-use crate::utils::{is_type_diagnostic_item, return_ty};
+use clippy_utils::{is_type_diagnostic_item, return_ty};
 
 impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
@@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
 
 # Dealing with macros
 
-There are several helpers in Clippy's utils to deal with macros:
+There are several helpers in [`clippy_utils`][utils] to deal with macros:
 
 - `in_macro()`: detect if the given span is expanded by a macro
 
@@ -199,4 +199,5 @@ assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
 [LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html
 [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_lints/src/utils/paths.rs
+[paths]: ../clippy_utils/src/paths.rs
+[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
index 3bc7758033b2836eb6035a54f31822b4617dffbc..c23dd926f621d1edd78409dde998be0ffbf7df4b 100644 (file)
@@ -1,6 +1,5 @@
-clippy 0.1.51 (7f5bb7fd0 2021-02-06)
+clippy 0.1.52 (697f3b6d4 2021-02-22)
 
-cargo-0.49.0//home/matthias/.rustup/toolchains/nightly-2021-02-03-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/macros/mod.rs:409:34 clippy::match_same_arms "this `match` has identical arm bodies"
 cargo-0.49.0/build.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.categories` metadata"
 cargo-0.49.0/build.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.keywords` metadata"
 cargo-0.49.0/src/bin/cargo/cli.rs:104:34 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
@@ -22,6 +21,7 @@ cargo-0.49.0/src/bin/cargo/commands/check.rs:1:5 clippy::wildcard_imports "usage
 cargo-0.49.0/src/bin/cargo/commands/clean.rs:1:5 clippy::wildcard_imports "usage of wildcard import"
 cargo-0.49.0/src/bin/cargo/commands/doc.rs:1:5 clippy::wildcard_imports "usage of wildcard import"
 cargo-0.49.0/src/bin/cargo/commands/fetch.rs:1:5 clippy::wildcard_imports "usage of wildcard import"
+cargo-0.49.0/src/bin/cargo/commands/fetch.rs:22:5 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
 cargo-0.49.0/src/bin/cargo/commands/fix.rs:1:5 clippy::wildcard_imports "usage of wildcard import"
 cargo-0.49.0/src/bin/cargo/commands/generate_lockfile.rs:1:5 clippy::wildcard_imports "usage of wildcard import"
 cargo-0.49.0/src/bin/cargo/commands/git_checkout.rs:1:5 clippy::wildcard_imports "usage of wildcard import"
@@ -99,10 +99,10 @@ cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:411:9 clippy::
 cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:420:69 clippy::doc_markdown "you should put `mode/target_kind` between ticks in the documentation"
 cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:423:19 clippy::doc_markdown "you should put `CrateTypes` between ticks in the documentation"
 cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:424:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:424:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:469:58 clippy::redundant_closure_for_method_calls "redundant closure found"
 cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:603:19 clippy::redundant_closure_for_method_calls "redundant closure found"
 cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:665:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
+cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:697:12 clippy::inconsistent_struct_constructor "inconsistent struct constructor"
 cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:82:31 clippy::doc_markdown "you should put `FileType` between ticks in the documentation"
 cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:83:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:84:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
@@ -151,7 +151,6 @@ cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:361:5 clippy::must_use_candi
 cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:374:43 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation"
 cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:378:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:383:41 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation"
-cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:384:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:384:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:391:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:397:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@@ -173,14 +172,13 @@ cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:481:5 clippy::missing_error
 cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:481:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:48:56 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation"
 cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:561:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:561:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:567:20 clippy::redundant_closure_for_method_calls "redundant closure found"
 cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:576:28 clippy::shadow_unrelated "`mut value` is being shadowed"
 cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:606:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:688:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
 cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:756:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
 cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:762:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
-cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:762:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`"
+cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:762:5 clippy::unnecessary_wraps "this function's return value is unnecessary"
 cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:823:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1021:51 clippy::redundant_closure_for_method_calls "redundant closure found"
 cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1656:16 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
@@ -236,7 +234,7 @@ cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:93:24 clippy::doc_markdown "yo
 cargo-0.49.0/src/cargo/core/compiler/links.rs:8:1 clippy::module_name_repetitions "item name ends with its containing module's name"
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:1016:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:1094:19 clippy::redundant_closure_for_method_calls "redundant closure found"
-cargo-0.49.0/src/cargo/core/compiler/mod.rs:1131:1 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`"
+cargo-0.49.0/src/cargo/core/compiler/mod.rs:1131:1 clippy::unnecessary_wraps "this function's return value is unnecessary"
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:1268:34 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison"
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:1277:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:179:1 clippy::too_many_lines "this function has too many lines (162/100)"
@@ -245,13 +243,13 @@ cargo-0.49.0/src/cargo/core/compiler/mod.rs:201:25 clippy::single_match_else "yo
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:267:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:324:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:364:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
-cargo-0.49.0/src/cargo/core/compiler/mod.rs:364:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`"
+cargo-0.49.0/src/cargo/core/compiler/mod.rs:364:5 clippy::unnecessary_wraps "this function's return value is unnecessary"
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:392:45 clippy::redundant_closure_for_method_calls "redundant closure found"
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:415:23 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:464:18 clippy::ptr_arg "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do."
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:488:61 clippy::ptr_arg "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do."
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:667:15 clippy::similar_names "binding's name is too similar to existing binding"
-cargo-0.49.0/src/cargo/core/compiler/mod.rs:693:1 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`"
+cargo-0.49.0/src/cargo/core/compiler/mod.rs:693:1 clippy::unnecessary_wraps "this function's return value is unnecessary"
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:725:42 clippy::match_same_arms "this `match` has identical arm bodies"
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:736:1 clippy::too_many_lines "this function has too many lines (141/100)"
 cargo-0.49.0/src/cargo/core/compiler/mod.rs:73:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@@ -290,7 +288,6 @@ cargo-0.49.0/src/cargo/core/compiler/unit_graph.rs:65:1 clippy::missing_errors_d
 cargo-0.49.0/src/cargo/core/compiler/unit_graph.rs:65:1 clippy::module_name_repetitions "item name ends with its containing module's name"
 cargo-0.49.0/src/cargo/core/dependency.rs:157:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/core/dependency.rs:182:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/core/dependency.rs:203:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/dependency.rs:203:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/dependency.rs:224:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/dependency.rs:23:1 clippy::struct_excessive_bools "more than 3 bools in a struct"
@@ -300,13 +297,10 @@ cargo-0.49.0/src/cargo/core/dependency.rs:274:5 clippy::must_use_candidate "this
 cargo-0.49.0/src/cargo/core/dependency.rs:278:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/dependency.rs:287:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/dependency.rs:291:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
-cargo-0.49.0/src/cargo/core/dependency.rs:296:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/dependency.rs:305:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/dependency.rs:311:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/dependency.rs:319:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
-cargo-0.49.0/src/cargo/core/dependency.rs:323:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/dependency.rs:337:75 clippy::redundant_closure_for_method_calls "redundant closure found"
-cargo-0.49.0/src/cargo/core/dependency.rs:379:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/dependency.rs:397:56 clippy::redundant_closure_for_method_calls "redundant closure found"
 cargo-0.49.0/src/cargo/core/dependency.rs:403:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/dependency.rs:408:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@@ -452,12 +446,10 @@ cargo-0.49.0/src/cargo/core/package.rs:287:1 clippy::module_name_repetitions "it
 cargo-0.49.0/src/cargo/core/package.rs:385:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/core/package.rs:421:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)"
 cargo-0.49.0/src/cargo/core/package.rs:425:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/core/package.rs:425:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/package.rs:452:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/core/package.rs:453:60 clippy::redundant_closure_for_method_calls "redundant closure found"
 cargo-0.49.0/src/cargo/core/package.rs:459:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/core/package.rs:473:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/core/package.rs:552:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/package.rs:587:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/core/package.rs:588:9 clippy::needless_question_mark "Question mark operator is useless here"
 cargo-0.49.0/src/cargo/core/package.rs:682:46 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value"
@@ -490,13 +482,12 @@ cargo-0.49.0/src/cargo/core/package_id_spec.rs:51:5 clippy::missing_errors_doc "
 cargo-0.49.0/src/cargo/core/package_id_spec.rs:51:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/package_id_spec.rs:77:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/core/package_id_spec.rs:88:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
-cargo-0.49.0/src/cargo/core/profiles.rs:1004:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/profiles.rs:1004:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/profiles.rs:1014:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/profiles.rs:1018:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/profiles.rs:1028:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/profiles.rs:106:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
-cargo-0.49.0/src/cargo/core/profiles.rs:143:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`"
+cargo-0.49.0/src/cargo/core/profiles.rs:143:5 clippy::unnecessary_wraps "this function's return value is unnecessary"
 cargo-0.49.0/src/cargo/core/profiles.rs:286:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/profiles.rs:286:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/profiles.rs:294:40 clippy::if_not_else "unnecessary boolean `not` operation"
@@ -522,9 +513,7 @@ cargo-0.49.0/src/cargo/core/registry.rs:19:5 clippy::missing_errors_doc "docs fo
 cargo-0.49.0/src/cargo/core/registry.rs:240:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/core/registry.rs:26:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/core/registry.rs:344:49 clippy::redundant_closure_for_method_calls "redundant closure found"
-cargo-0.49.0/src/cargo/core/registry.rs:358:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/registry.rs:369:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
-cargo-0.49.0/src/cargo/core/registry.rs:424:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/registry.rs:424:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/registry.rs:49:1 clippy::module_name_repetitions "item name ends with its containing module's name"
 cargo-0.49.0/src/cargo/core/registry.rs:520:17 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
@@ -538,7 +527,6 @@ cargo-0.49.0/src/cargo/core/resolver/context.rs:274:53 clippy::redundant_closure
 cargo-0.49.0/src/cargo/core/resolver/context.rs:42:1 clippy::module_name_repetitions "item name starts with its containing module's name"
 cargo-0.49.0/src/cargo/core/resolver/context.rs:74:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/resolver/encode.rs:156:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/core/resolver/encode.rs:156:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/resolver/encode.rs:156:5 clippy::too_many_lines "this function has too many lines (164/100)"
 cargo-0.49.0/src/cargo/core/resolver/encode.rs:339:17 clippy::match_wildcard_for_single_variants "wildcard match will miss any future added variants"
 cargo-0.49.0/src/cargo/core/resolver/encode.rs:438:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
@@ -596,7 +584,6 @@ cargo-0.49.0/src/cargo/core/resolver/resolve.rs:255:5 clippy::must_use_candidate
 cargo-0.49.0/src/cargo/core/resolver/resolve.rs:259:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/resolver/resolve.rs:263:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/resolver/resolve.rs:269:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
-cargo-0.49.0/src/cargo/core/resolver/resolve.rs:273:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/resolver/resolve.rs:273:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/resolver/resolve.rs:274:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead"
 cargo-0.49.0/src/cargo/core/resolver/resolve.rs:280:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@@ -639,6 +626,7 @@ cargo-0.49.0/src/cargo/core/shell.rs:314:5 clippy::must_use_candidate "this meth
 cargo-0.49.0/src/cargo/core/shell.rs:322:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/shell.rs:330:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/core/shell.rs:345:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
+cargo-0.49.0/src/cargo/core/shell.rs:459:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
 cargo-0.49.0/src/cargo/core/shell.rs:98:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/source/mod.rs:103:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/core/source/mod.rs:247:1 clippy::module_name_repetitions "item name starts with its containing module's name"
@@ -682,7 +670,6 @@ cargo-0.49.0/src/cargo/core/source/source_id.rs:241:5 clippy::must_use_candidate
 cargo-0.49.0/src/cargo/core/source/source_id.rs:252:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/source/source_id.rs:257:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/source/source_id.rs:262:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/core/source/source_id.rs:262:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/core/source/source_id.rs:305:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/source/source_id.rs:310:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/core/source/source_id.rs:318:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@@ -780,7 +767,6 @@ cargo-0.49.0/src/cargo/core/workspace.rs:849:5 clippy::missing_panics_doc "docs
 cargo-0.49.0/src/cargo/core/workspace.rs:893:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/core/workspace.rs:906:24 clippy::redundant_else "redundant else block"
 cargo-0.49.0/src/cargo/core/workspace.rs:932:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/core/workspace.rs:932:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/lib.rs:177:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/lib.rs:177:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/lib.rs:180:36 clippy::redundant_closure_for_method_calls "redundant closure found"
@@ -808,7 +794,6 @@ cargo-0.49.0/src/cargo/ops/cargo_compile.rs:249:1 clippy::missing_errors_doc "do
 cargo-0.49.0/src/cargo/ops/cargo_compile.rs:258:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/ops/cargo_compile.rs:267:16 clippy::needless_question_mark "Question mark operator is useless here"
 cargo-0.49.0/src/cargo/ops/cargo_compile.rs:275:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/ops/cargo_compile.rs:275:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/ops/cargo_compile.rs:275:1 clippy::too_many_lines "this function has too many lines (219/100)"
 cargo-0.49.0/src/cargo/ops/cargo_compile.rs:468:9 clippy::default_trait_access "calling `std::collections::HashMap::default()` is more clear than this expression"
 cargo-0.49.0/src/cargo/ops/cargo_compile.rs:548:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@@ -824,7 +809,6 @@ cargo-0.49.0/src/cargo/ops/cargo_compile.rs:612:21 clippy::doc_markdown "you sho
 cargo-0.49.0/src/cargo/ops/cargo_compile.rs:613:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/ops/cargo_compile.rs:618:9 clippy::similar_names "binding's name is too similar to existing binding"
 cargo-0.49.0/src/cargo/ops/cargo_compile.rs:641:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
-cargo-0.49.0/src/cargo/ops/cargo_compile.rs:652:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/ops/cargo_compile.rs:652:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/ops/cargo_compile.rs:655:50 clippy::match_same_arms "this `match` has identical arm bodies"
 cargo-0.49.0/src/cargo/ops/cargo_compile.rs:673:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@@ -879,6 +863,7 @@ cargo-0.49.0/src/cargo/ops/cargo_package.rs:394:5 clippy::items_after_statements
 cargo-0.49.0/src/cargo/ops/cargo_package.rs:425:61 clippy::redundant_closure_for_method_calls "redundant closure found"
 cargo-0.49.0/src/cargo/ops/cargo_package.rs:459:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
 cargo-0.49.0/src/cargo/ops/cargo_package.rs:66:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
+cargo-0.49.0/src/cargo/ops/cargo_package.rs:69:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
 cargo-0.49.0/src/cargo/ops/cargo_package.rs:93:20 clippy::if_not_else "unnecessary boolean `not` operation"
 cargo-0.49.0/src/cargo/ops/cargo_pkgid.rs:5:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:14:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@@ -893,7 +878,6 @@ cargo-0.49.0/src/cargo/ops/cargo_run.rs:37:16 clippy::redundant_else "redundant
 cargo-0.49.0/src/cargo/ops/cargo_run.rs:53:9 clippy::if_not_else "unnecessary boolean `not` operation"
 cargo-0.49.0/src/cargo/ops/cargo_run.rs:65:16 clippy::redundant_else "redundant else block"
 cargo-0.49.0/src/cargo/ops/cargo_run.rs:9:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/ops/cargo_run.rs:9:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/ops/cargo_test.rs:16:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/ops/cargo_test.rs:43:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/ops/cargo_test.rs:84:17 clippy::similar_names "binding's name is too similar to existing binding"
@@ -904,7 +888,7 @@ cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:233:21 clippy::si
 cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:244:22 clippy::doc_markdown "you should put `PackageId` between ticks in the documentation"
 cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:244:63 clippy::doc_markdown "you should put `PackageId` between ticks in the documentation"
 cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:253:17 clippy::if_not_else "unnecessary boolean `not` operation"
-cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:370:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`"
+cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:370:5 clippy::unnecessary_wraps "this function's return value is unnecessary"
 cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:505:8 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead"
 cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:525:10 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body"
 cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:542:27 clippy::redundant_closure_for_method_calls "redundant closure found"
@@ -967,7 +951,6 @@ cargo-0.49.0/src/cargo/ops/registry.rs:794:16 clippy::single_match_else "you see
 cargo-0.49.0/src/cargo/ops/registry.rs:828:14 clippy::doc_markdown "you should put `SourceId` between ticks in the documentation"
 cargo-0.49.0/src/cargo/ops/registry.rs:848:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::module_name_repetitions "item name starts with its containing module's name"
 cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::too_many_lines "this function has too many lines (137/100)"
 cargo-0.49.0/src/cargo/ops/resolve.rs:241:28 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
@@ -997,6 +980,7 @@ cargo-0.49.0/src/cargo/ops/tree/mod.rs:21:1 clippy::struct_excessive_bools "more
 cargo-0.49.0/src/cargo/ops/tree/mod.rs:360:30 clippy::match_same_arms "this `match` has identical arm bodies"
 cargo-0.49.0/src/cargo/ops/tree/mod.rs:58:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/ops/vendor.rs:14:1 clippy::module_name_repetitions "item name starts with its containing module's name"
+cargo-0.49.0/src/cargo/ops/vendor.rs:215:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
 cargo-0.49.0/src/cargo/ops/vendor.rs:21:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/ops/vendor.rs:21:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/ops/vendor.rs:314:34 clippy::match_same_arms "this `match` has identical arm bodies"
@@ -1017,7 +1001,6 @@ cargo-0.49.0/src/cargo/sources/directory.rs:14:1 clippy::module_name_repetitions
 cargo-0.49.0/src/cargo/sources/directory.rs:90:56 clippy::redundant_closure_for_method_calls "redundant closure found"
 cargo-0.49.0/src/cargo/sources/git/source.rs:14:1 clippy::module_name_repetitions "item name ends with its containing module's name"
 cargo-0.49.0/src/cargo/sources/git/source.rs:25:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/sources/git/source.rs:25:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/sources/git/source.rs:49:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/sources/git/source.rs:53:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/sources/git/source.rs:53:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
@@ -1032,7 +1015,6 @@ cargo-0.49.0/src/cargo/sources/git/utils.rs:184:5 clippy::missing_errors_doc "do
 cargo-0.49.0/src/cargo/sources/git/utils.rs:188:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/sources/git/utils.rs:242:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/sources/git/utils.rs:253:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/sources/git/utils.rs:253:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/sources/git/utils.rs:262:13 clippy::if_not_else "unnecessary boolean `not` operation"
 cargo-0.49.0/src/cargo/sources/git/utils.rs:289:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/sources/git/utils.rs:294:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@@ -1062,7 +1044,6 @@ cargo-0.49.0/src/cargo/sources/path.rs:429:5 clippy::missing_errors_doc "docs fo
 cargo-0.49.0/src/cargo/sources/path.rs:460:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/sources/path.rs:473:43 clippy::redundant_closure_for_method_calls "redundant closure found"
 cargo-0.49.0/src/cargo/sources/path.rs:482:43 clippy::redundant_closure_for_method_calls "redundant closure found"
-cargo-0.49.0/src/cargo/sources/path.rs:55:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/sources/path.rs:63:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/sources/path.rs:77:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/sources/path.rs:98:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@@ -1142,7 +1123,6 @@ cargo-0.49.0/src/cargo/util/config/mod.rs:100:71 clippy::doc_markdown "you shoul
 cargo-0.49.0/src/cargo/util/config/mod.rs:1049:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/util/config/mod.rs:1064:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/util/config/mod.rs:1090:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/util/config/mod.rs:1090:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/util/config/mod.rs:1166:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/util/config/mod.rs:1179:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/util/config/mod.rs:1181:33 clippy::needless_question_mark "Question mark operator is useless here"
@@ -1157,7 +1137,6 @@ cargo-0.49.0/src/cargo/util/config/mod.rs:1225:5 clippy::missing_errors_doc "doc
 cargo-0.49.0/src/cargo/util/config/mod.rs:1229:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/util/config/mod.rs:124:1 clippy::struct_excessive_bools "more than 3 bools in a struct"
 cargo-0.49.0/src/cargo/util/config/mod.rs:1254:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-cargo-0.49.0/src/cargo/util/config/mod.rs:1263:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/util/config/mod.rs:1279:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/util/config/mod.rs:1281:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
 cargo-0.49.0/src/cargo/util/config/mod.rs:1323:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
@@ -1224,7 +1203,6 @@ cargo-0.49.0/src/cargo/util/dependency_queue.rs:151:5 clippy::must_use_candidate
 cargo-0.49.0/src/cargo/util/dependency_queue.rs:156:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/util/dependency_queue.rs:168:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/util/dependency_queue.rs:46:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
-cargo-0.49.0/src/cargo/util/dependency_queue.rs:66:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/util/dependency_queue.rs:91:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
 cargo-0.49.0/src/cargo/util/diagnostic_server.rs:218:1 clippy::module_name_repetitions "item name ends with its containing module's name"
 cargo-0.49.0/src/cargo/util/diagnostic_server.rs:230:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@@ -1238,7 +1216,6 @@ cargo-0.49.0/src/cargo/util/errors.rs:143:5 clippy::must_use_candidate "this met
 cargo-0.49.0/src/cargo/util/errors.rs:150:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/util/errors.rs:15:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/util/errors.rs:237:5 clippy::pub_enum_variant_names "variant name ends with the enum's name"
-cargo-0.49.0/src/cargo/util/errors.rs:245:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/util/errors.rs:245:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/util/errors.rs:321:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/util/errors.rs:328:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@@ -1263,12 +1240,11 @@ cargo-0.49.0/src/cargo/util/flock.rs:335:23 clippy::cast_possible_truncation "ca
 cargo-0.49.0/src/cargo/util/flock.rs:335:23 clippy::cast_sign_loss "casting `i64` to `u32` may lose the sign of the value"
 cargo-0.49.0/src/cargo/util/flock.rs:335:44 clippy::cast_possible_truncation "casting `i64` to `u32` may truncate the value"
 cargo-0.49.0/src/cargo/util/flock.rs:379:35 clippy::match_same_arms "this `match` has identical arm bodies"
-cargo-0.49.0/src/cargo/util/flock.rs:37:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/util/flock.rs:37:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
-cargo-0.49.0/src/cargo/util/flock.rs:43:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/util/flock.rs:43:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/util/flock.rs:52:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/util/flock.rs:52:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
+cargo-0.49.0/src/cargo/util/flock.rs:96:17 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
 cargo-0.49.0/src/cargo/util/graph.rs:10:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/util/graph.rs:41:51 clippy::redundant_closure_for_method_calls "redundant closure found"
 cargo-0.49.0/src/cargo/util/graph.rs:45:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@@ -1331,8 +1307,8 @@ cargo-0.49.0/src/cargo/util/paths.rs:445:1 clippy::missing_errors_doc "docs for
 cargo-0.49.0/src/cargo/util/paths.rs:459:45 clippy::redundant_closure_for_method_calls "redundant closure found"
 cargo-0.49.0/src/cargo/util/paths.rs:469:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/util/paths.rs:469:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
+cargo-0.49.0/src/cargo/util/paths.rs:514:5 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
 cargo-0.49.0/src/cargo/util/paths.rs:54:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute"
-cargo-0.49.0/src/cargo/util/paths.rs:61:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 cargo-0.49.0/src/cargo/util/paths.rs:61:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/util/paths.rs:63:19 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else"
 cargo-0.49.0/src/cargo/util/paths.rs:88:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@@ -1346,6 +1322,7 @@ cargo-0.49.0/src/cargo/util/process_builder.rs:185:5 clippy::missing_errors_doc
 cargo-0.49.0/src/cargo/util/process_builder.rs:190:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/util/process_builder.rs:218:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/util/process_builder.rs:218:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
+cargo-0.49.0/src/cargo/util/process_builder.rs:278:22 clippy::inconsistent_struct_constructor "inconsistent struct constructor"
 cargo-0.49.0/src/cargo/util/process_builder.rs:307:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 cargo-0.49.0/src/cargo/util/process_builder.rs:343:39 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body"
 cargo-0.49.0/src/cargo/util/progress.rs:122:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
@@ -1386,6 +1363,7 @@ cargo-0.49.0/src/cargo/util/rustc.rs:115:5 clippy::doc_markdown "you should put
 cargo-0.49.0/src/cargo/util/rustc.rs:162:17 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
 cargo-0.49.0/src/cargo/util/rustc.rs:39:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/util/sha256.rs:10:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
+cargo-0.49.0/src/cargo/util/sha256.rs:16:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
 cargo-0.49.0/src/cargo/util/sha256.rs:20:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/util/sha256.rs:31:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 cargo-0.49.0/src/cargo/util/sha256.rs:40:24 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore"
@@ -1451,6 +1429,7 @@ iron-0.6.1/src/iron.rs:133:5 clippy::missing_errors_doc "docs for function retur
 iron-0.6.1/src/iron.rs:143:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 iron-0.6.1/src/iron.rs:149:13 clippy::redundant_field_names "redundant field names in struct initialization"
 iron-0.6.1/src/iron.rs:167:49 clippy::similar_names "binding's name is too similar to existing binding"
+iron-0.6.1/src/iron.rs:196:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
 iron-0.6.1/src/iron.rs:80:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 iron-0.6.1/src/iron.rs:85:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 iron-0.6.1/src/iron.rs:90:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
@@ -1991,18 +1970,15 @@ log-0.4.11/src/lib.rs:1118:5 clippy::must_use_candidate "this method could have
 log-0.4.11/src/lib.rs:1177:1 clippy::inline_always "you have declared `#[inline(always)]` on `max_level`. This is usually a bad idea"
 log-0.4.11/src/lib.rs:1178:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute"
 log-0.4.11/src/lib.rs:1306:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-log-0.4.11/src/lib.rs:1306:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 log-0.4.11/src/lib.rs:1358:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute"
 log-0.4.11/src/lib.rs:1359:5 clippy::if_not_else "unnecessary `!=` operation"
 log-0.4.11/src/lib.rs:1407:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute"
-log-0.4.11/src/lib.rs:329:27 clippy::derive_hash_xor_eq "you are deriving `Hash` but have implemented `PartialEq` explicitly"
 log-0.4.11/src/lib.rs:356:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type"
 log-0.4.11/src/lib.rs:448:12 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation"
 log-0.4.11/src/lib.rs:500:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 log-0.4.11/src/lib.rs:506:28 clippy::trivially_copy_pass_by_ref "this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)"
 log-0.4.11/src/lib.rs:506:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 log-0.4.11/src/lib.rs:506:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
-log-0.4.11/src/lib.rs:520:27 clippy::derive_hash_xor_eq "you are deriving `Hash` but have implemented `PartialEq` explicitly"
 log-0.4.11/src/lib.rs:538:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type"
 log-0.4.11/src/lib.rs:653:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 log-0.4.11/src/lib.rs:661:21 clippy::trivially_copy_pass_by_ref "this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)"
@@ -2112,7 +2088,6 @@ puffin-02dd4a3/puffin/src/data.rs:137:24 clippy::match_same_arms "this `match` h
 puffin-02dd4a3/puffin/src/data.rs:177:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 puffin-02dd4a3/puffin/src/data.rs:211:21 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers"
 puffin-02dd4a3/puffin/src/data.rs:24:5 clippy::wildcard_imports "usage of wildcard import"
-puffin-02dd4a3/puffin/src/data.rs:75:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 puffin-02dd4a3/puffin/src/lib.rs:113:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 puffin-02dd4a3/puffin/src/lib.rs:147:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 puffin-02dd4a3/puffin/src/lib.rs:147:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
@@ -2146,7 +2121,6 @@ quote-1.0.7/src/ext.rs:10:1 clippy::module_name_repetitions "item name ends with
 quote-1.0.7/src/ext.rs:7:5 clippy::doc_markdown "you should put `TokenStream` between ticks in the documentation"
 quote-1.0.7/src/ident_fragment.rs:13:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 quote-1.0.7/src/ident_fragment.rs:51:31 clippy::manual_strip "stripping a prefix manually"
-quote-1.0.7/src/runtime.rs:332:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 quote-1.0.7/src/runtime.rs:52:5 clippy::module_name_repetitions "item name ends with its containing module's name"
 quote-1.0.7/src/runtime.rs:63:5 clippy::module_name_repetitions "item name ends with its containing module's name"
 quote-1.0.7/src/runtime.rs:66:33 clippy::doc_markdown "you should put `DoesNotHaveIter` between ticks in the documentation"
@@ -2183,7 +2157,6 @@ rand-0.7.3/src/distributions/binomial.rs:233:32 clippy::cast_precision_loss "cas
 rand-0.7.3/src/distributions/binomial.rs:234:27 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)"
 rand-0.7.3/src/distributions/binomial.rs:251:22 clippy::cast_sign_loss "casting `i64` to `u64` may lose the sign of the value"
 rand-0.7.3/src/distributions/binomial.rs:255:9 clippy::if_not_else "unnecessary `!=` operation"
-rand-0.7.3/src/distributions/binomial.rs:35:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 rand-0.7.3/src/distributions/binomial.rs:35:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 rand-0.7.3/src/distributions/binomial.rs:45:17 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)"
 rand-0.7.3/src/distributions/binomial.rs:46:5 clippy::cast_possible_truncation "casting `f64` to `i64` may truncate the value"
@@ -2194,25 +2167,18 @@ rand-0.7.3/src/distributions/binomial.rs:81:21 clippy::cast_precision_loss "cast
 rand-0.7.3/src/distributions/binomial.rs:82:32 clippy::cast_possible_truncation "casting `u64` to `i32` may truncate the value"
 rand-0.7.3/src/distributions/binomial.rs:88:26 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)"
 rand-0.7.3/src/distributions/binomial.rs:99:21 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)"
-rand-0.7.3/src/distributions/cauchy.rs:33:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 rand-0.7.3/src/distributions/cauchy.rs:33:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 rand-0.7.3/src/distributions/dirichlet.rs:52:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 rand-0.7.3/src/distributions/dirichlet.rs:64:32 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore"
 rand-0.7.3/src/distributions/dirichlet.rs:65:23 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore"
-rand-0.7.3/src/distributions/exponential.rs:76:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 rand-0.7.3/src/distributions/exponential.rs:76:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 rand-0.7.3/src/distributions/float.rs:73:1 clippy::module_name_repetitions "item name ends with its containing module's name"
 rand-0.7.3/src/distributions/gamma.rs:13:5 clippy::enum_glob_use "usage of wildcard import for enum variants"
 rand-0.7.3/src/distributions/gamma.rs:14:5 clippy::enum_glob_use "usage of wildcard import for enum variants"
-rand-0.7.3/src/distributions/gamma.rs:189:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 rand-0.7.3/src/distributions/gamma.rs:189:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
-rand-0.7.3/src/distributions/gamma.rs:230:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 rand-0.7.3/src/distributions/gamma.rs:230:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
-rand-0.7.3/src/distributions/gamma.rs:259:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 rand-0.7.3/src/distributions/gamma.rs:259:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
-rand-0.7.3/src/distributions/gamma.rs:287:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 rand-0.7.3/src/distributions/gamma.rs:287:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
-rand-0.7.3/src/distributions/gamma.rs:90:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 rand-0.7.3/src/distributions/gamma.rs:90:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 rand-0.7.3/src/distributions/integer.rs:23:9 clippy::cast_possible_truncation "casting `u32` to `u8` may truncate the value"
 rand-0.7.3/src/distributions/integer.rs:30:9 clippy::cast_possible_truncation "casting `u32` to `u16` may truncate the value"
@@ -2226,7 +2192,6 @@ rand-0.7.3/src/distributions/normal.rs:47:25 clippy::unseparated_literal_suffix
 rand-0.7.3/src/distributions/normal.rs:48:25 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore"
 rand-0.7.3/src/distributions/other.rs:89:9 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value"
 rand-0.7.3/src/distributions/pareto.rs:32:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
-rand-0.7.3/src/distributions/poisson.rs:35:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 rand-0.7.3/src/distributions/poisson.rs:35:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 rand-0.7.3/src/distributions/poisson.rs:87:30 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value"
 rand-0.7.3/src/distributions/poisson.rs:87:30 clippy::cast_sign_loss "casting `f64` to `u64` may lose the sign of the value"
@@ -2316,6 +2281,7 @@ rand-0.7.3/src/rngs/adapter/reseeding.rs:112:5 clippy::inline_always "you have d
 rand-0.7.3/src/rngs/adapter/reseeding.rs:117:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u64`. This is usually a bad idea"
 rand-0.7.3/src/rngs/adapter/reseeding.rs:198:13 clippy::cast_possible_wrap "casting `u64` to `i64` may wrap around the value"
 rand-0.7.3/src/rngs/adapter/reseeding.rs:231:9 clippy::cast_possible_wrap "casting `usize` to `isize` may wrap around the value"
+rand-0.7.3/src/rngs/adapter/reseeding.rs:249:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
 rand-0.7.3/src/rngs/adapter/reseeding.rs:27:28 clippy::doc_markdown "you should put `ChaCha` between ticks in the documentation"
 rand-0.7.3/src/rngs/adapter/reseeding.rs:79:1 clippy::module_name_repetitions "item name starts with its containing module's name"
 rand-0.7.3/src/rngs/entropy.rs:24:1 clippy::module_name_repetitions "item name starts with its containing module's name"
@@ -2344,7 +2310,6 @@ rand-0.7.3/src/seq/index.rs:139:13 clippy::enum_glob_use "usage of wildcard impo
 rand-0.7.3/src/seq/index.rs:159:1 clippy::module_name_repetitions "item name starts with its containing module's name"
 rand-0.7.3/src/seq/index.rs:171:13 clippy::enum_glob_use "usage of wildcard import for enum variants"
 rand-0.7.3/src/seq/index.rs:180:13 clippy::enum_glob_use "usage of wildcard import for enum variants"
-rand-0.7.3/src/seq/index.rs:213:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 rand-0.7.3/src/seq/index.rs:223:18 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers"
 rand-0.7.3/src/seq/index.rs:224:18 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers"
 rand-0.7.3/src/seq/index.rs:233:25 clippy::cast_precision_loss "casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)"
@@ -2369,14 +2334,12 @@ rand-0.7.3/src/seq/mod.rs:45:4 clippy::needless_doctest_main "needless `fn main`
 rand-0.7.3/src/seq/mod.rs:527:26 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers"
 rand_core-0.6.0/src/block.rs:117:1 clippy::module_name_repetitions "item name starts with its containing module's name"
 rand_core-0.6.0/src/block.rs:153:5 clippy::inline_always "you have declared `#[inline(always)]` on `index`. This is usually a bad idea"
-rand_core-0.6.0/src/block.rs:168:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 rand_core-0.6.0/src/block.rs:230:5 clippy::inline_always "you have declared `#[inline(always)]` on `try_fill_bytes`. This is usually a bad idea"
 rand_core-0.6.0/src/block.rs:240:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_seed`. This is usually a bad idea"
 rand_core-0.6.0/src/block.rs:245:5 clippy::inline_always "you have declared `#[inline(always)]` on `seed_from_u64`. This is usually a bad idea"
 rand_core-0.6.0/src/block.rs:250:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_rng`. This is usually a bad idea"
 rand_core-0.6.0/src/block.rs:280:1 clippy::module_name_repetitions "item name starts with its containing module's name"
 rand_core-0.6.0/src/block.rs:319:5 clippy::inline_always "you have declared `#[inline(always)]` on `index`. This is usually a bad idea"
-rand_core-0.6.0/src/block.rs:335:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 rand_core-0.6.0/src/block.rs:405:5 clippy::inline_always "you have declared `#[inline(always)]` on `try_fill_bytes`. This is usually a bad idea"
 rand_core-0.6.0/src/block.rs:415:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_seed`. This is usually a bad idea"
 rand_core-0.6.0/src/block.rs:420:5 clippy::inline_always "you have declared `#[inline(always)]` on `seed_from_u64`. This is usually a bad idea"
@@ -2386,8 +2349,6 @@ rand_core-0.6.0/src/block.rs:68:1 clippy::module_name_repetitions "item name sta
 rand_core-0.6.0/src/error.rs:106:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 rand_core-0.6.0/src/error.rs:87:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 rand_core-0.6.0/src/error.rs:95:74 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value"
-rand_core-0.6.0/src/le.rs:18:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
-rand_core-0.6.0/src/le.rs:27:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 rand_core-0.6.0/src/lib.rs:179:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
 rand_core-0.6.0/src/lib.rs:301:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute"
 rand_core-0.6.0/src/lib.rs:303:26 clippy::unreadable_literal "long literal lacking separators"
@@ -2433,6 +2394,7 @@ rayon-1.5.0/src/iter/chain.rs:58:17 clippy::shadow_unrelated "`b` is being shado
 rayon-1.5.0/src/iter/chain.rs:78:14 clippy::shadow_unrelated "`a` is being shadowed"
 rayon-1.5.0/src/iter/chain.rs:78:17 clippy::shadow_unrelated "`b` is being shadowed"
 rayon-1.5.0/src/iter/chain.rs:97:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
+rayon-1.5.0/src/iter/chunks.rs:29:9 clippy::inconsistent_struct_constructor "inconsistent struct constructor"
 rayon-1.5.0/src/iter/chunks.rs:3:5 clippy::wildcard_imports "usage of wildcard import"
 rayon-1.5.0/src/iter/chunks.rs:4:5 clippy::wildcard_imports "usage of wildcard import"
 rayon-1.5.0/src/iter/chunks.rs:77:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope"
@@ -2699,7 +2661,6 @@ regex-1.3.2/src/compile.rs:1040:38 clippy::cast_possible_truncation "casting `u1
 regex-1.3.2/src/compile.rs:1051:25 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore"
 regex-1.3.2/src/compile.rs:1071:8 clippy::cast_lossless "casting `u32` to `u64` may become silently lossy if you later change the type"
 regex-1.3.2/src/compile.rs:112:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section"
-regex-1.3.2/src/compile.rs:112:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 regex-1.3.2/src/compile.rs:154:30 clippy::redundant_closure_for_method_calls "redundant closure found"
 regex-1.3.2/src/compile.rs:156:30 clippy::redundant_closure_for_method_calls "redundant closure found"
 regex-1.3.2/src/compile.rs:185:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`"
@@ -3114,10 +3075,6 @@ regex-1.3.2/src/utf8.rs:85:19 clippy::cast_lossless "casting `u8` to `u32` may b
 regex-1.3.2/src/utf8.rs:92:23 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four"
 regex-1.3.2/src/utf8.rs:92:9 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four"
 regex-1.3.2/src/utf8.rs:97:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four"
-ripgrep-12.1.1//home/matthias/.rustup/toolchains/nightly-2021-02-03-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:30:27 clippy::match_same_arms "this `match` has identical arm bodies"
-ripgrep-12.1.1//home/matthias/.rustup/toolchains/nightly-2021-02-03-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:30:27 clippy::match_same_arms "this `match` has identical arm bodies"
-ripgrep-12.1.1//home/matthias/.rustup/toolchains/nightly-2021-02-03-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:30:27 clippy::match_same_arms "this `match` has identical arm bodies"
-ripgrep-12.1.1//home/matthias/.rustup/toolchains/nightly-2021-02-03-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:30:27 clippy::match_same_arms "this `match` has identical arm bodies"
 ripgrep-12.1.1/build.rs:133:19 clippy::option_as_ref_deref "called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `githash.as_deref()` instead"
 ripgrep-12.1.1/build.rs:18:18 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
 ripgrep-12.1.1/build.rs:225:14 clippy::redundant_closure_for_method_calls "redundant closure found"
@@ -3171,6 +3128,7 @@ ripgrep-12.1.1/crates/core/args.rs:1524:5 clippy::unnecessary_wraps "this functi
 ripgrep-12.1.1/crates/core/args.rs:1635:14 clippy::doc_markdown "you should put `values_of_lossy` between ticks in the documentation"
 ripgrep-12.1.1/crates/core/args.rs:1693:41 clippy::redundant_closure_for_method_calls "redundant closure found"
 ripgrep-12.1.1/crates/core/args.rs:1770:17 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers"
+ripgrep-12.1.1/crates/core/args.rs:1829:5 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
 ripgrep-12.1.1/crates/core/args.rs:287:13 clippy::similar_names "binding's name is too similar to existing binding"
 ripgrep-12.1.1/crates/core/args.rs:33:1 clippy::single_component_path_imports "this import is redundant"
 ripgrep-12.1.1/crates/core/args.rs:34:1 clippy::single_component_path_imports "this import is redundant"
@@ -3189,6 +3147,8 @@ ripgrep-12.1.1/crates/core/config.rs:58:6 clippy::type_complexity "very complex
 ripgrep-12.1.1/crates/core/config.rs:79:6 clippy::type_complexity "very complex type used. Consider factoring parts into `type` definitions"
 ripgrep-12.1.1/crates/core/logger.rs:11:30 clippy::doc_markdown "you should put `max_level` between ticks in the documentation"
 ripgrep-12.1.1/crates/core/logger.rs:15:16 clippy::redundant_static_lifetimes "constants have by default a `'static` lifetime"
+ripgrep-12.1.1/crates/core/main.rs:114:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
+ripgrep-12.1.1/crates/core/main.rs:189:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
 ripgrep-12.1.1/crates/core/main.rs:55:19 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body"
 ripgrep-12.1.1/crates/core/main.rs:56:9 clippy::enum_glob_use "usage of wildcard import for enum variants"
 ripgrep-12.1.1/crates/core/messages.rs:46:1 clippy::module_name_repetitions "item name ends with its containing module's name"
@@ -3216,19 +3176,18 @@ ripgrep-12.1.1/crates/core/search.rs:533:36 clippy::cast_lossless "casting `u32`
 ripgrep-12.1.1/crates/core/search.rs:533:5 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)"
 ripgrep-12.1.1/crates/core/subject.rs:20:1 clippy::module_name_repetitions "item name starts with its containing module's name"
 ripgrep-12.1.1/crates/core/subject.rs:4:1 clippy::single_component_path_imports "this import is redundant"
+serde-1.0.118/src/de/mod.rs:1592:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
+serde-1.0.118/src/de/mod.rs:1616:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
+serde-1.0.118/src/de/mod.rs:1627:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
+serde-1.0.118/src/de/mod.rs:1638:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
+serde-1.0.118/src/de/mod.rs:1649:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
+serde-1.0.118/src/de/mod.rs:952:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
+serde-1.0.118/src/de/mod.rs:986:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`"
 syn-1.0.54/build.rs:1:null clippy::cargo_common_metadata "package `syn` is missing `package.keywords` metadata"
-syn-1.0.54/build.rs:1:null clippy::multiple_crate_versions "could not read cargo metadata: `cargo metadata` exited with an error:  Downloading crates ...\n  Downloaded syn-test-suite v0.0.0\nerror: failed to verify the checksum of `syn-test-suite v0.0.0`"
-syn-1.0.54/src/generics.rs:174:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 syn-1.0.54/src/lib.rs:1:null clippy::cargo_common_metadata "package `syn` is missing `package.keywords` metadata"
-syn-1.0.54/src/lib.rs:1:null clippy::multiple_crate_versions "could not read cargo metadata: `cargo metadata` exited with an error:  Downloading crates ...\n  Downloaded syn-test-suite v0.0.0\nerror: failed to verify the checksum of `syn-test-suite v0.0.0`"
 syn-1.0.54/src/lit.rs:1397:40 clippy::redundant_else "redundant else block"
 syn-1.0.54/src/lit.rs:1405:28 clippy::redundant_else "redundant else block"
 syn-1.0.54/src/lit.rs:1485:32 clippy::redundant_else "redundant else block"
-syn-1.0.54/src/lit.rs:343:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
-syn-1.0.54/src/lit.rs:437:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
-syn-1.0.54/src/lit.rs:916:9 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
-syn-1.0.54/src/token.rs:974:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
-syn-1.0.54/src/token.rs:996:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section"
 unicode-xid-0.2.1/src/lib.rs:1:null clippy::cargo_common_metadata "package `unicode-xid` is missing `package.categories` metadata"
 unicode-xid-0.2.1/src/lib.rs:56:11 clippy::upper_case_acronyms "name `UnicodeXID` contains a capitalized acronym"
 unicode-xid-0.2.1/src/lib.rs:57:64 clippy::doc_markdown "you should put `XID_Start` between ticks in the documentation"
@@ -3248,7 +3207,6 @@ xsv-0.13.0/src/cmd/cat.rs:7:16 clippy::redundant_static_lifetimes "statics have
 xsv-0.13.0/src/cmd/count.rs:32:9 clippy::similar_names "binding's name is too similar to existing binding"
 xsv-0.13.0/src/cmd/count.rs:38:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
 xsv-0.13.0/src/cmd/count.rs:42:33 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore"
-xsv-0.13.0/src/cmd/count.rs:50:5 clippy::unit_arg "passing a unit value to a function"
 xsv-0.13.0/src/cmd/count.rs:7:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime"
 xsv-0.13.0/src/cmd/fixlengths.rs:45:9 clippy::similar_names "binding's name is too similar to existing binding"
 xsv-0.13.0/src/cmd/fixlengths.rs:50:18 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"
@@ -3324,7 +3282,7 @@ xsv-0.13.0/src/cmd/sort.rs:48:9 clippy::similar_names "binding's name is too sim
 xsv-0.13.0/src/cmd/sort.rs:91:14 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods"
 xsv-0.13.0/src/cmd/split.rs:14:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime"
 xsv-0.13.0/src/cmd/split.rs:61:9 clippy::similar_names "binding's name is too similar to existing binding"
-xsv-0.13.0/src/cmd/split.rs:94:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`"
+xsv-0.13.0/src/cmd/split.rs:94:5 clippy::unnecessary_wraps "this function's return value is unnecessary"
 xsv-0.13.0/src/cmd/split.rs:96:14 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body"
 xsv-0.13.0/src/cmd/split.rs:99:13 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers"
 xsv-0.13.0/src/cmd/stats.rs:110:36 clippy::redundant_closure_for_method_calls "redundant closure found"
@@ -3438,14 +3396,12 @@ clippy::redundant_slicing 1
 clippy::same_item_push 1
 clippy::should_implement_trait 1
 clippy::stable_sort_primitive 1
-clippy::unit_arg 1
 clippy::unnecessary_lazy_evaluations 1
 clippy::unsafe_derive_deserialize 1
 clippy::used_underscore_binding 1
 clippy::verbose_bit_mask 1
 clippy::while_let_on_iterator 1
 clippy::comparison_to_empty 2
-clippy::derive_hash_xor_eq 2
 clippy::expl_impl_clone_on_copy 2
 clippy::filter_map 2
 clippy::len_zero 2
@@ -3463,10 +3419,10 @@ clippy::write_with_newline 2
 clippy::filter_map_next 3
 clippy::fn_params_excessive_bools 3
 clippy::if_same_then_else 3
+clippy::inconsistent_struct_constructor 3
 clippy::mut_mut 3
 clippy::ptr_arg 3
 clippy::zero_ptr 3
-clippy::let_underscore_drop 4
 clippy::too_many_arguments 4
 clippy::explicit_iter_loop 5
 clippy::field_reassign_with_default 5
@@ -3488,11 +3444,11 @@ clippy::range_plus_one 7
 clippy::invalid_upcast_comparisons 8
 clippy::needless_question_mark 8
 clippy::wrong_self_convention 8
+clippy::multiple_crate_versions 9
 clippy::manual_range_contains 10
 clippy::match_wildcard_for_single_variants 10
 clippy::missing_safety_doc 10
 clippy::needless_doctest_main 10
-clippy::multiple_crate_versions 11
 clippy::needless_lifetimes 12
 clippy::cargo_common_metadata 13
 clippy::shadow_unrelated 13
@@ -3511,6 +3467,7 @@ clippy::struct_excessive_bools 20
 clippy::redundant_static_lifetimes 21
 clippy::default_trait_access 22
 clippy::cast_lossless 23
+clippy::let_underscore_drop 23
 clippy::trivially_copy_pass_by_ref 26
 clippy::redundant_else 29
 clippy::too_many_lines 32
@@ -3519,11 +3476,11 @@ clippy::enum_glob_use 40
 clippy::unseparated_literal_suffix 41
 clippy::cast_precision_loss 44
 clippy::single_match_else 45
+clippy::missing_panics_doc 54
 clippy::inline_always 59
-clippy::match_same_arms 65
+clippy::match_same_arms 60
 clippy::similar_names 78
 clippy::cast_possible_truncation 95
-clippy::missing_panics_doc 108
 clippy::redundant_field_names 111
 clippy::redundant_closure_for_method_calls 135
 clippy::items_after_statements 139
@@ -3533,3 +3490,4 @@ clippy::doc_markdown 178
 clippy::missing_errors_doc 343
 clippy::unreadable_literal 365
 clippy::must_use_candidate 565
+ICEs:
index e73da595e19df9cb1397be1a06e140e016038ffc..865043b46d179516040b478cfdff377561dc8816 100644 (file)
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-02-11"
+channel = "nightly-2021-02-25"
 components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
index c0b40add1096d0654033524626bca9d687e349e5..0594663786ce212a3d7547d79e4efe56f7825234 100644 (file)
@@ -66,8 +66,8 @@ fn third_party_crates() -> String {
 fn default_config() -> compiletest::Config {
     let mut config = compiletest::Config::default();
 
-    if let Ok(name) = env::var("TESTNAME") {
-        config.filter = Some(name);
+    if let Ok(filters) = env::var("TESTNAME") {
+        config.filters = filters.split(',').map(std::string::ToString::to_string).collect();
     }
 
     if let Some(path) = option_env!("RUSTC_LIB_PATH") {
@@ -167,7 +167,7 @@ fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>
 fn run_ui_cargo(config: &mut compiletest::Config) {
     fn run_tests(
         config: &compiletest::Config,
-        filter: &Option<String>,
+        filters: &[String],
         mut tests: Vec<tester::TestDescAndFn>,
     ) -> Result<bool, io::Error> {
         let mut result = true;
@@ -181,9 +181,10 @@ fn run_tests(
 
             // Use the filter if provided
             let dir_path = dir.path();
-            match &filter {
-                Some(name) if !dir_path.ends_with(name) => continue,
-                _ => {},
+            for filter in filters {
+                if !dir_path.ends_with(filter) {
+                    continue;
+                }
             }
 
             for case in fs::read_dir(&dir_path)? {
@@ -243,8 +244,7 @@ fn run_tests(
 
     let current_dir = env::current_dir().unwrap();
     let conf_dir = var("CLIPPY_CONF_DIR").unwrap_or_default();
-    let filter = env::var("TESTNAME").ok();
-    let res = run_tests(&config, &filter, tests);
+    let res = run_tests(&config, &config.filters, tests);
     env::set_current_dir(current_dir).unwrap();
     set_var("CLIPPY_CONF_DIR", conf_dir);
 
index 7ccd0b54845d197abe4385b28a74cc84a444ae34..d83080b69f5e5311b68bc8ccd21d3c42bf107f51 100644 (file)
@@ -1,4 +1,4 @@
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `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`, `unreadable-literal-lint-fractions`, `cargo-ignore-publish`, `third-party` at line 5 column 1
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `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`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1
 
 error: aborting due to previous error
 
diff --git a/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/clippy.toml b/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/clippy.toml
new file mode 100644 (file)
index 0000000..cc94ec5
--- /dev/null
@@ -0,0 +1 @@
+upper-case-acronyms-aggressive = true
diff --git a/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs b/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs
new file mode 100644 (file)
index 0000000..fdf8905
--- /dev/null
@@ -0,0 +1,22 @@
+#![warn(clippy::upper_case_acronyms)]
+
+struct HTTPResponse; // not linted by default, but with cfg option
+
+struct CString; // not linted
+
+enum Flags {
+    NS, // not linted
+    CWR,
+    ECE,
+    URG,
+    ACK,
+    PSH,
+    RST,
+    SYN,
+    FIN,
+}
+
+struct GCCLLVMSomething; // linted with cfg option, beware that lint suggests `GccllvmSomething` instead of
+                         // `GccLlvmSomething`
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr b/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr
new file mode 100644 (file)
index 0000000..1cc59dc
--- /dev/null
@@ -0,0 +1,70 @@
+error: name `HTTPResponse` contains a capitalized acronym
+  --> $DIR/upper_case_acronyms.rs:3:8
+   |
+LL | struct HTTPResponse; // not linted by default, but with cfg option
+   |        ^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `HttpResponse`
+   |
+   = note: `-D clippy::upper-case-acronyms` implied by `-D warnings`
+
+error: name `NS` contains a capitalized acronym
+  --> $DIR/upper_case_acronyms.rs:8:5
+   |
+LL |     NS, // not linted
+   |     ^^ help: consider making the acronym lowercase, except the initial letter (notice the capitalization): `Ns`
+
+error: name `CWR` contains a capitalized acronym
+  --> $DIR/upper_case_acronyms.rs:9:5
+   |
+LL |     CWR,
+   |     ^^^ help: consider making the acronym lowercase, except the initial letter: `Cwr`
+
+error: name `ECE` contains a capitalized acronym
+  --> $DIR/upper_case_acronyms.rs:10:5
+   |
+LL |     ECE,
+   |     ^^^ help: consider making the acronym lowercase, except the initial letter: `Ece`
+
+error: name `URG` contains a capitalized acronym
+  --> $DIR/upper_case_acronyms.rs:11:5
+   |
+LL |     URG,
+   |     ^^^ help: consider making the acronym lowercase, except the initial letter: `Urg`
+
+error: name `ACK` contains a capitalized acronym
+  --> $DIR/upper_case_acronyms.rs:12:5
+   |
+LL |     ACK,
+   |     ^^^ help: consider making the acronym lowercase, except the initial letter (notice the capitalization): `Ack`
+
+error: name `PSH` contains a capitalized acronym
+  --> $DIR/upper_case_acronyms.rs:13:5
+   |
+LL |     PSH,
+   |     ^^^ help: consider making the acronym lowercase, except the initial letter: `Psh`
+
+error: name `RST` contains a capitalized acronym
+  --> $DIR/upper_case_acronyms.rs:14:5
+   |
+LL |     RST,
+   |     ^^^ help: consider making the acronym lowercase, except the initial letter: `Rst`
+
+error: name `SYN` contains a capitalized acronym
+  --> $DIR/upper_case_acronyms.rs:15:5
+   |
+LL |     SYN,
+   |     ^^^ help: consider making the acronym lowercase, except the initial letter: `Syn`
+
+error: name `FIN` contains a capitalized acronym
+  --> $DIR/upper_case_acronyms.rs:16:5
+   |
+LL |     FIN,
+   |     ^^^ help: consider making the acronym lowercase, except the initial letter: `Fin`
+
+error: name `GCCLLVMSomething` contains a capitalized acronym
+  --> $DIR/upper_case_acronyms.rs:19:8
+   |
+LL | struct GCCLLVMSomething; // linted with cfg option, beware that lint suggests `GccllvmSomething` instead of
+   |        ^^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `GccllvmSomething`
+
+error: aborting due to 11 previous errors
+
index 24891682d368d0bfa2f167f88512040928442dab..aebeaf346799d3767a483533b9ff1e2f67c17783 100644 (file)
@@ -41,3 +41,15 @@ fn lint() {
         }
     }
 }
+
+#[proc_macro_derive(StructAUseSelf)]
+pub fn derive_use_self(_input: TokenStream) -> proc_macro::TokenStream {
+    quote! {
+        struct A;
+        impl A {
+            fn new() -> A {
+                A
+            }
+        }
+    }
+}
index acbabfa20d737495708db8f9cc1380a24e9349c5..2856943b9be8001a31c172480e16d35e6580abd9 100644 (file)
@@ -44,4 +44,13 @@ fn macro_in_closure() {
     }
 }
 
-fn main() {}
+#[rustfmt::skip]
+fn main() {
+    let mut range = 0..10;
+    range.all(|i| {i < 10} );
+
+    let v = vec![1, 2, 3];
+    if v.into_iter().any(|x| {x == 4}) {
+        println!("contains 4!");
+    }
+}
index fa4bc30e933a2a0ba4252edafe3b7ef9e9e5cbf7..c69a46f0a77ee908e908d67c821782776c6f8cc1 100644 (file)
@@ -65,4 +65,13 @@ fn main() {
     else {
         println!("!")
     }
+
+    if x == "hello" {
+        print!("Hello ");
+    } else {
+        #[cfg(not(roflol))]
+        if y == "world" {
+            println!("world!")
+        }
+    }
 }
index bf6c1d1f894d7807a5c03a00b5e08f02f6ffa30c..1359c7eb6278e6dbda3ee8f937fe8acc835a4258 100644 (file)
@@ -79,4 +79,13 @@ fn main() {
             println!("!")
         }
     }
+
+    if x == "hello" {
+        print!("Hello ");
+    } else {
+        #[cfg(not(roflol))]
+        if y == "world" {
+            println!("world!")
+        }
+    }
 }
index efd4187947b201221058dbede468a1f1a76f841f..e4c088bf6f03f3bf5402af00d503bff56965340f 100644 (file)
@@ -138,4 +138,11 @@ fn main() {
 
     // Fix #5962
     if matches!(true, true) && matches!(true, true) {}
+
+    if true {
+        #[cfg(not(teehee))]
+        if true {
+            println!("Hello world!");
+        }
+    }
 }
index 657f32d38a32bc1a3714c8de089e6ade5a52c628..d6cf01c8319400c844ecedf0f2eea401c3c67fdf 100644 (file)
@@ -154,4 +154,11 @@ fn truth() -> bool { true }
     if matches!(true, true) {
         if matches!(true, true) {}
     }
+
+    if true {
+        #[cfg(not(teehee))]
+        if true {
+            println!("Hello world!");
+        }
+    }
 }
index 3294da7e8146f51abf10979c63958c70e975fbd7..55467cf4229de4b9e5607d003a93f010bfd9eefd 100644 (file)
@@ -232,6 +232,14 @@ enum E<T> {
             };
         }
     }
+    let _: &dyn std::any::Any = match &Some(Some(1)) {
+        Some(e) => match e {
+            Some(e) => e,
+            e => e,
+        },
+        // else branch looks the same but the binding is different
+        e => e,
+    };
 }
 
 fn make<T>() -> T {
index 63ac6a1613dc648ce44a90a139d768d18e2a7788..77978884900893f816cc016ea570e93a33c32688 100644 (file)
@@ -1,4 +1,4 @@
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match.rs:7:20
    |
 LL |           Ok(val) => match val {
@@ -9,15 +9,15 @@ LL | |         },
    | |_________^
    |
    = note: `-D clippy::collapsible-match` implied by `-D warnings`
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match.rs:7:12
    |
 LL |         Ok(val) => match val {
-   |            ^^^ Replace this binding
+   |            ^^^ replace this binding
 LL |             Some(n) => foo(n),
    |             ^^^^^^^ with this pattern
 
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match.rs:16:20
    |
 LL |           Ok(val) => match val {
@@ -27,15 +27,15 @@ LL | |             _ => return,
 LL | |         },
    | |_________^
    |
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match.rs:16:12
    |
 LL |         Ok(val) => match val {
-   |            ^^^ Replace this binding
+   |            ^^^ replace this binding
 LL |             Some(n) => foo(n),
    |             ^^^^^^^ with this pattern
 
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match.rs:25:9
    |
 LL | /         if let Some(n) = val {
@@ -43,15 +43,15 @@ LL | |             take(n);
 LL | |         }
    | |_________^
    |
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match.rs:24:15
    |
 LL |     if let Ok(val) = res_opt {
-   |               ^^^ Replace this binding
+   |               ^^^ replace this binding
 LL |         if let Some(n) = val {
    |                ^^^^^^^ with this pattern
 
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match.rs:32:9
    |
 LL | /         if let Some(n) = val {
@@ -61,15 +61,15 @@ LL | |             return;
 LL | |         }
    | |_________^
    |
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match.rs:31:15
    |
 LL |     if let Ok(val) = res_opt {
-   |               ^^^ Replace this binding
+   |               ^^^ replace this binding
 LL |         if let Some(n) = val {
    |                ^^^^^^^ with this pattern
 
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match.rs:43:9
    |
 LL | /         match val {
@@ -78,16 +78,16 @@ LL | |             _ => (),
 LL | |         }
    | |_________^
    |
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match.rs:42:15
    |
 LL |     if let Ok(val) = res_opt {
-   |               ^^^ Replace this binding
+   |               ^^^ replace this binding
 LL |         match val {
 LL |             Some(n) => foo(n),
    |             ^^^^^^^ with this pattern
 
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match.rs:52:13
    |
 LL | /             if let Some(n) = val {
@@ -95,15 +95,15 @@ LL | |                 take(n);
 LL | |             }
    | |_____________^
    |
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match.rs:51:12
    |
 LL |         Ok(val) => {
-   |            ^^^ Replace this binding
+   |            ^^^ replace this binding
 LL |             if let Some(n) = val {
    |                    ^^^^^^^ with this pattern
 
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match.rs:61:9
    |
 LL | /         match val {
@@ -112,16 +112,16 @@ LL | |             _ => return,
 LL | |         }
    | |_________^
    |
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match.rs:60:15
    |
 LL |     if let Ok(val) = res_opt {
-   |               ^^^ Replace this binding
+   |               ^^^ replace this binding
 LL |         match val {
 LL |             Some(n) => foo(n),
    |             ^^^^^^^ with this pattern
 
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match.rs:72:13
    |
 LL | /             if let Some(n) = val {
@@ -131,15 +131,15 @@ LL | |                 return;
 LL | |             }
    | |_____________^
    |
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match.rs:71:12
    |
 LL |         Ok(val) => {
-   |            ^^^ Replace this binding
+   |            ^^^ replace this binding
 LL |             if let Some(n) = val {
    |                    ^^^^^^^ with this pattern
 
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match.rs:83:20
    |
 LL |           Ok(val) => match val {
@@ -149,15 +149,15 @@ LL | |             None => return,
 LL | |         },
    | |_________^
    |
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match.rs:83:12
    |
 LL |         Ok(val) => match val {
-   |            ^^^ Replace this binding
+   |            ^^^ replace this binding
 LL |             Some(n) => foo(n),
    |             ^^^^^^^ with this pattern
 
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match.rs:92:22
    |
 LL |           Some(val) => match val {
@@ -167,11 +167,11 @@ LL | |             _ => return,
 LL | |         },
    | |_________^
    |
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match.rs:92:14
    |
 LL |         Some(val) => match val {
-   |              ^^^ Replace this binding
+   |              ^^^ replace this binding
 LL |             Some(n) => foo(n),
    |             ^^^^^^^ with this pattern
 
index b2eb457d1732622898250d32158c696db3e2a3b3..c8a445ef369d9be9c796ad3c43552119394e8781 100644 (file)
@@ -1,4 +1,4 @@
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match2.rs:8:34
    |
 LL |               Ok(val) if make() => match val {
@@ -9,15 +9,15 @@ LL | |             },
    | |_____________^
    |
    = note: `-D clippy::collapsible-match` implied by `-D warnings`
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match2.rs:8:16
    |
 LL |             Ok(val) if make() => match val {
-   |                ^^^ Replace this binding
+   |                ^^^ replace this binding
 LL |                 Some(n) => foo(n),
    |                 ^^^^^^^ with this pattern
 
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match2.rs:15:24
    |
 LL |               Ok(val) => match val {
@@ -27,15 +27,15 @@ LL | |                 _ => return,
 LL | |             },
    | |_____________^
    |
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match2.rs:15:16
    |
 LL |             Ok(val) => match val {
-   |                ^^^ Replace this binding
+   |                ^^^ replace this binding
 LL |                 Some(n) => foo(n),
    |                 ^^^^^^^ with this pattern
 
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match2.rs:29:29
    |
 LL |                       $pat => match $e {
@@ -48,16 +48,16 @@ LL | |                     },
 LL |           mac!(res_opt => Ok(val), val => Some(n), foo(n));
    |           ------------------------------------------------- in this macro invocation
    |
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match2.rs:41:28
    |
 LL |         mac!(res_opt => Ok(val), val => Some(n), foo(n));
    |                            ^^^          ^^^^^^^ with this pattern
    |                            |
-   |                            Replace this binding
+   |                            replace this binding
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match2.rs:46:20
    |
 LL |           Some(s) => match *s {
@@ -67,15 +67,15 @@ LL | |             _ => (),
 LL | |         },
    | |_________^
    |
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match2.rs:46:14
    |
 LL |         Some(s) => match *s {
-   |              ^ Replace this binding
+   |              ^ replace this binding
 LL |             [n] => foo(n),
    |             ^^^ with this pattern
 
-error: Unnecessary nested match
+error: unnecessary nested match
   --> $DIR/collapsible_match2.rs:55:24
    |
 LL |           Some(ref s) => match &*s {
@@ -85,11 +85,11 @@ LL | |             _ => (),
 LL | |         },
    | |_________^
    |
-help: The outer pattern can be modified to include the inner pattern.
+help: the outer pattern can be modified to include the inner pattern
   --> $DIR/collapsible_match2.rs:55:14
    |
 LL |         Some(ref s) => match &*s {
-   |              ^^^^^ Replace this binding
+   |              ^^^^^ replace this binding
 LL |             [n] => foo(n),
    |             ^^^ with this pattern
 
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6179.rs b/src/tools/clippy/tests/ui/crashes/ice-6179.rs
new file mode 100644 (file)
index 0000000..f8c866a
--- /dev/null
@@ -0,0 +1,21 @@
+//! This is a minimal reproducer for the ICE in https://github.com/rust-lang/rust-clippy/pull/6179.
+//! The ICE is mainly caused by using `hir_ty_to_ty`. See the discussion in the PR for details.
+
+#![warn(clippy::use_self)]
+#![allow(dead_code)]
+
+struct Foo {}
+
+impl Foo {
+    fn foo() -> Self {
+        impl Foo {
+            fn bar() {}
+        }
+
+        let _: _ = 1;
+
+        Self {}
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback.rs b/src/tools/clippy/tests/ui/default_numeric_fallback.rs
new file mode 100644 (file)
index 0000000..0b37589
--- /dev/null
@@ -0,0 +1,135 @@
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+
+mod basic_expr {
+    fn test() {
+        // Should lint unsuffixed literals typed `i32`.
+        let x = 22;
+        let x = [1, 2, 3];
+        let x = if true { (1, 2) } else { (3, 4) };
+        let x = match 1 {
+            1 => 1,
+            _ => 2,
+        };
+
+        // Should lint unsuffixed literals typed `f64`.
+        let x = 0.12;
+
+        // Should NOT lint suffixed literals.
+        let x = 22_i32;
+        let x = 0.12_f64;
+
+        // Should NOT lint literals in init expr if `Local` has a type annotation.
+        let x: f64 = 0.1;
+        let x: [i32; 3] = [1, 2, 3];
+        let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
+        let x: _ = 1;
+    }
+}
+
+mod nested_local {
+    fn test() {
+        let x: _ = {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1
+        };
+
+        let x: _ = if true {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            1
+        } else {
+            // Should lint this because this literal is not bound to any types.
+            let y = 1;
+
+            // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+            2
+        };
+    }
+}
+
+mod function_def {
+    fn ret_i32() -> i32 {
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        1
+    }
+
+    fn test() {
+        // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
+        // type.
+        let f = || -> _ { 1 };
+
+        // Even though the output type is specified,
+        // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+        let f = || -> i32 { 1 };
+    }
+}
+
+mod function_calls {
+    fn concrete_arg(x: i32) {}
+
+    fn generic_arg<T>(t: T) {}
+
+    fn test() {
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        concrete_arg(1);
+
+        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+        generic_arg(1);
+
+        // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+        let x: _ = generic_arg(1);
+    }
+}
+
+mod struct_ctor {
+    struct ConcreteStruct {
+        x: i32,
+    }
+
+    struct GenericStruct<T> {
+        x: T,
+    }
+
+    fn test() {
+        // Should NOT lint this because the field type is bound to a concrete type.
+        ConcreteStruct { x: 1 };
+
+        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+        GenericStruct { x: 1 };
+
+        // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+        let _ = GenericStruct { x: 1 };
+    }
+}
+
+mod method_calls {
+    struct StructForMethodCallTest {}
+
+    impl StructForMethodCallTest {
+        fn concrete_arg(&self, x: i32) {}
+
+        fn generic_arg<T>(&self, t: T) {}
+    }
+
+    fn test() {
+        let s = StructForMethodCallTest {};
+
+        // Should NOT lint this because the argument type is bound to a concrete type.
+        s.concrete_arg(1);
+
+        // Should lint this because the argument type is bound to a concrete type.
+        s.generic_arg(1);
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback.stderr
new file mode 100644 (file)
index 0000000..b31aa4e
--- /dev/null
@@ -0,0 +1,148 @@
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:10:17
+   |
+LL |         let x = 22;
+   |                 ^^ help: consider adding suffix: `22_i32`
+   |
+   = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:11:18
+   |
+LL |         let x = [1, 2, 3];
+   |                  ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:11:21
+   |
+LL |         let x = [1, 2, 3];
+   |                     ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:11:24
+   |
+LL |         let x = [1, 2, 3];
+   |                        ^ help: consider adding suffix: `3_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:12:28
+   |
+LL |         let x = if true { (1, 2) } else { (3, 4) };
+   |                            ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:12:31
+   |
+LL |         let x = if true { (1, 2) } else { (3, 4) };
+   |                               ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:12:44
+   |
+LL |         let x = if true { (1, 2) } else { (3, 4) };
+   |                                            ^ help: consider adding suffix: `3_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:12:47
+   |
+LL |         let x = if true { (1, 2) } else { (3, 4) };
+   |                                               ^ help: consider adding suffix: `4_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:13:23
+   |
+LL |         let x = match 1 {
+   |                       ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:14:13
+   |
+LL |             1 => 1,
+   |             ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:14:18
+   |
+LL |             1 => 1,
+   |                  ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:15:18
+   |
+LL |             _ => 2,
+   |                  ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:19:17
+   |
+LL |         let x = 0.12;
+   |                 ^^^^ help: consider adding suffix: `0.12_f64`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:37:21
+   |
+LL |             let y = 1;
+   |                     ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:45:21
+   |
+LL |             let y = 1;
+   |                     ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:51:21
+   |
+LL |             let y = 1;
+   |                     ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:63:9
+   |
+LL |         1
+   |         ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:69:27
+   |
+LL |         let f = || -> _ { 1 };
+   |                           ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:73:29
+   |
+LL |         let f = || -> i32 { 1 };
+   |                             ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:87:21
+   |
+LL |         generic_arg(1);
+   |                     ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:90:32
+   |
+LL |         let x: _ = generic_arg(1);
+   |                                ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:108:28
+   |
+LL |         GenericStruct { x: 1 };
+   |                            ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:111:36
+   |
+LL |         let _ = GenericStruct { x: 1 };
+   |                                    ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+  --> $DIR/default_numeric_fallback.rs:131:23
+   |
+LL |         s.generic_arg(1);
+   |                       ^ help: consider adding suffix: `1_i32`
+
+error: aborting due to 24 previous errors
+
index e30970ed952732f4d49dea1d7df15df2f0e1bdd8..d2c666bd2901c7fbe34e9f7daeeeab83deb8344a 100644 (file)
@@ -50,11 +50,23 @@ fn test_units() {
 }
 
 /// This tests allowed identifiers.
+/// KiB MiB GiB TiB PiB EiB
 /// DirectX
 /// ECMAScript
+/// GPLv2 GPLv3
+/// GitHub GitLab
+/// IPv4 IPv6
+/// ClojureScript CoffeeScript JavaScript PureScript TypeScript
+/// NaN NaNs
 /// OAuth GraphQL
+/// OCaml
+/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS
 /// WebGL
+/// TensorFlow
+/// TrueType
+/// iOS macOS
 /// TeX LaTeX BibTeX BibLaTeX
+/// MinGW
 /// CamelCase (see also #2395)
 /// be_sure_we_got_to_the_end_of_it
 fn test_allowed() {
index e1c1aa85a60e1d55724e2d0db0cf4abe7dd13a11..7eab8a85f093d6f1de5a51e517605338f98fee33 100644 (file)
@@ -55,133 +55,133 @@ LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
-  --> $DIR/doc.rs:59:5
+  --> $DIR/doc.rs:71:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: you should put `link_with_underscores` between ticks in the documentation
-  --> $DIR/doc.rs:63:22
+  --> $DIR/doc.rs:75:22
    |
 LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823.
    |                      ^^^^^^^^^^^^^^^^^^^^^
 
 error: you should put `inline_link2` between ticks in the documentation
-  --> $DIR/doc.rs:66:21
+  --> $DIR/doc.rs:78:21
    |
 LL | /// It can also be [inline_link2].
    |                     ^^^^^^^^^^^^
 
 error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
-  --> $DIR/doc.rs:76:5
+  --> $DIR/doc.rs:88:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: you should put `CamelCaseThing` between ticks in the documentation
-  --> $DIR/doc.rs:84:8
+  --> $DIR/doc.rs:96:8
    |
 LL | /// ## CamelCaseThing
    |        ^^^^^^^^^^^^^^
 
 error: you should put `CamelCaseThing` between ticks in the documentation
-  --> $DIR/doc.rs:87:7
+  --> $DIR/doc.rs:99:7
    |
 LL | /// # CamelCaseThing
    |       ^^^^^^^^^^^^^^
 
 error: you should put `CamelCaseThing` between ticks in the documentation
-  --> $DIR/doc.rs:89:22
+  --> $DIR/doc.rs:101:22
    |
 LL | /// Not a title #897 CamelCaseThing
    |                      ^^^^^^^^^^^^^^
 
 error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
-  --> $DIR/doc.rs:90:5
+  --> $DIR/doc.rs:102:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
-  --> $DIR/doc.rs:97:5
+  --> $DIR/doc.rs:109:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
-  --> $DIR/doc.rs:110:5
+  --> $DIR/doc.rs:122:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: you should put `FooBar` between ticks in the documentation
-  --> $DIR/doc.rs:121:43
+  --> $DIR/doc.rs:133:43
    |
 LL | /** E.g., serialization of an empty list: FooBar
    |                                           ^^^^^^
 
 error: you should put `BarQuz` between ticks in the documentation
-  --> $DIR/doc.rs:126:5
+  --> $DIR/doc.rs:138:5
    |
 LL | And BarQuz too.
    |     ^^^^^^
 
 error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
-  --> $DIR/doc.rs:127:1
+  --> $DIR/doc.rs:139:1
    |
 LL | be_sure_we_got_to_the_end_of_it
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: you should put `FooBar` between ticks in the documentation
-  --> $DIR/doc.rs:132:43
+  --> $DIR/doc.rs:144:43
    |
 LL | /** E.g., serialization of an empty list: FooBar
    |                                           ^^^^^^
 
 error: you should put `BarQuz` between ticks in the documentation
-  --> $DIR/doc.rs:137:5
+  --> $DIR/doc.rs:149:5
    |
 LL | And BarQuz too.
    |     ^^^^^^
 
 error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
-  --> $DIR/doc.rs:138:1
+  --> $DIR/doc.rs:150:1
    |
 LL | be_sure_we_got_to_the_end_of_it
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
-  --> $DIR/doc.rs:149:5
+  --> $DIR/doc.rs:161:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: you should put bare URLs between `<`/`>` or make a proper Markdown link
-  --> $DIR/doc.rs:176:13
+  --> $DIR/doc.rs:188:13
    |
 LL | /// Not ok: http://www.unicode.org
    |             ^^^^^^^^^^^^^^^^^^^^^^
 
 error: you should put bare URLs between `<`/`>` or make a proper Markdown link
-  --> $DIR/doc.rs:177:13
+  --> $DIR/doc.rs:189:13
    |
 LL | /// Not ok: https://www.unicode.org
    |             ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: you should put bare URLs between `<`/`>` or make a proper Markdown link
-  --> $DIR/doc.rs:178:13
+  --> $DIR/doc.rs:190:13
    |
 LL | /// Not ok: http://www.unicode.org/
    |             ^^^^^^^^^^^^^^^^^^^^^^
 
 error: you should put bare URLs between `<`/`>` or make a proper Markdown link
-  --> $DIR/doc.rs:179:13
+  --> $DIR/doc.rs:191:13
    |
 LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: you should put `mycrate::Collection` between ticks in the documentation
-  --> $DIR/doc.rs:182:22
+  --> $DIR/doc.rs:194:22
    |
 LL | /// An iterator over mycrate::Collection's values.
    |                      ^^^^^^^^^^^^^^^^^^^
index 7ef932f367b101a61e47375f0b580482a42ab4e2..3008c2d5b85381710f3235acd7fc9c8ac2e6cf86 100644 (file)
@@ -28,6 +28,15 @@ pub fn inner_body(opt: Option<u32>) {
     });
 }
 
+/// This needs to be documented
+pub fn unreachable_and_panic() {
+    if true {
+        unreachable!()
+    } else {
+        panic!()
+    }
+}
+
 /// This is documented
 ///
 /// # Panics
@@ -69,6 +78,19 @@ pub fn todo_documented() {
     todo!()
 }
 
+/// This is documented
+///
+/// # Panics
+///
+/// We still need to do this part
+pub fn unreachable_amd_panic_documented() {
+    if true {
+        unreachable!()
+    } else {
+        panic!()
+    }
+}
+
 /// This is okay because it is private
 fn unwrap_private() {
     let result = Err("Hi");
@@ -93,3 +115,8 @@ fn inner_body_private(opt: Option<u32>) {
         }
     });
 }
+
+/// This is okay because unreachable
+pub fn unreachable() {
+    unreachable!("This function panics")
+}
index c0c4e9e4fa7eec6d682eddd4f7b214cfeded6712..287148690d27af84c408679e7ba500b92ddad4b1 100644 (file)
@@ -63,5 +63,24 @@ LL |             panic!()
    |             ^^^^^^^^
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 4 previous errors
+error: docs for function which may panic missing `# Panics` section
+  --> $DIR/doc_panics.rs:32:1
+   |
+LL | / pub fn unreachable_and_panic() {
+LL | |     if true {
+LL | |         unreachable!()
+LL | |     } else {
+LL | |         panic!()
+LL | |     }
+LL | | }
+   | |_^
+   |
+note: first possible panic found here
+  --> $DIR/doc_panics.rs:36:9
+   |
+LL |         panic!()
+   |         ^^^^^^^^
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 5 previous errors
 
index 89d99dcf0c867b7ccaec389dd674c2841ea042df..4fefc0b43f1d93dbf55a153e4db1bd559baeab63 100644 (file)
@@ -133,4 +133,17 @@ pub enum NetworkLayer {
     Layer3,
 }
 
+// should lint suggesting `IData`, not only `Data` (see #4639)
+enum IDataRequest {
+    PutIData(String),
+    GetIData(String),
+    DeleteUnpubIData(String),
+}
+
+enum HIDataRequest {
+    PutHIData(String),
+    GetHIData(String),
+    DeleteUnpubHIData(String),
+}
+
 fn main() {}
index b1d481190ff53bdfa68e41934fa668c49edb05f6..ab7fff4507aaa6719db234e5b5bbe436f8e8b9b9 100644 (file)
@@ -97,5 +97,29 @@ LL | | }
    = note: `-D clippy::pub-enum-variant-names` implied by `-D warnings`
    = help: remove the prefixes and use full paths to the variants instead of glob imports
 
-error: aborting due to 10 previous errors
+error: all variants have the same postfix: `IData`
+  --> $DIR/enum_variants.rs:137:1
+   |
+LL | / enum IDataRequest {
+LL | |     PutIData(String),
+LL | |     GetIData(String),
+LL | |     DeleteUnpubIData(String),
+LL | | }
+   | |_^
+   |
+   = help: remove the postfixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same postfix: `HIData`
+  --> $DIR/enum_variants.rs:143:1
+   |
+LL | / enum HIDataRequest {
+LL | |     PutHIData(String),
+LL | |     GetHIData(String),
+LL | |     DeleteUnpubHIData(String),
+LL | | }
+   | |_^
+   |
+   = help: remove the postfixes and use full paths to the variants instead of glob imports
+
+error: aborting due to 12 previous errors
 
diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.rs b/src/tools/clippy/tests/ui/from_str_radix_10.rs
new file mode 100644 (file)
index 0000000..2f2ea04
--- /dev/null
@@ -0,0 +1,52 @@
+#![warn(clippy::from_str_radix_10)]
+
+mod some_mod {
+    // fake function that shouldn't trigger the lint
+    pub fn from_str_radix(_: &str, _: u32) -> Result<(), std::num::ParseIntError> {
+        unimplemented!()
+    }
+}
+
+// fake function that shouldn't trigger the lint
+fn from_str_radix(_: &str, _: u32) -> Result<(), std::num::ParseIntError> {
+    unimplemented!()
+}
+
+// to test parenthesis addition
+struct Test;
+
+impl std::ops::Add<Test> for Test {
+    type Output = &'static str;
+
+    fn add(self, _: Self) -> Self::Output {
+        "304"
+    }
+}
+
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+    // all of these should trigger the lint
+    u32::from_str_radix("30", 10)?;
+    i64::from_str_radix("24", 10)?;
+    isize::from_str_radix("100", 10)?;
+    u8::from_str_radix("7", 10)?;
+    u16::from_str_radix(&("10".to_owned() + "5"), 10)?;
+    i128::from_str_radix(Test + Test, 10)?;
+
+    let string = "300";
+    i32::from_str_radix(string, 10)?;
+
+    let stringier = "400".to_string();
+    i32::from_str_radix(&stringier, 10)?;
+
+    // none of these should trigger the lint
+    u16::from_str_radix("20", 3)?;
+    i32::from_str_radix("45", 12)?;
+    usize::from_str_radix("10", 16)?;
+    i128::from_str_radix("10", 13)?;
+    some_mod::from_str_radix("50", 10)?;
+    some_mod::from_str_radix("50", 6)?;
+    from_str_radix("50", 10)?;
+    from_str_radix("50", 6)?;
+
+    Ok(())
+}
diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.stderr b/src/tools/clippy/tests/ui/from_str_radix_10.stderr
new file mode 100644 (file)
index 0000000..471bf52
--- /dev/null
@@ -0,0 +1,52 @@
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+  --> $DIR/from_str_radix_10.rs:28:5
+   |
+LL |     u32::from_str_radix("30", 10)?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"30".parse::<u32>()`
+   |
+   = note: `-D clippy::from-str-radix-10` implied by `-D warnings`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+  --> $DIR/from_str_radix_10.rs:29:5
+   |
+LL |     i64::from_str_radix("24", 10)?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::<i64>()`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+  --> $DIR/from_str_radix_10.rs:30:5
+   |
+LL |     isize::from_str_radix("100", 10)?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::<isize>()`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+  --> $DIR/from_str_radix_10.rs:31:5
+   |
+LL |     u8::from_str_radix("7", 10)?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::<u8>()`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+  --> $DIR/from_str_radix_10.rs:32:5
+   |
+LL |     u16::from_str_radix(&("10".to_owned() + "5"), 10)?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(("10".to_owned() + "5")).parse::<u16>()`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+  --> $DIR/from_str_radix_10.rs:33:5
+   |
+LL |     i128::from_str_radix(Test + Test, 10)?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::<i128>()`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+  --> $DIR/from_str_radix_10.rs:36:5
+   |
+LL |     i32::from_str_radix(string, 10)?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::<i32>()`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+  --> $DIR/from_str_radix_10.rs:39:5
+   |
+LL |     i32::from_str_radix(&stringier, 10)?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::<i32>()`
+
+error: aborting due to 8 previous errors
+
index e83ce47e563080b5a25e0bad69d0d57fc3c581be..a2ff1b741ca25e5699ff8d9f2453325ed7e4b9f0 100644 (file)
@@ -12,7 +12,7 @@ fn if_same_then_else2() -> Result<&'static str, ()> {
     if true {
         for _ in &[42] {
             let foo: &Option<_> = &Some::<u8>(42);
-            if true {
+            if foo.is_some() {
                 break;
             } else {
                 continue;
@@ -21,8 +21,8 @@ fn if_same_then_else2() -> Result<&'static str, ()> {
     } else {
         //~ ERROR same body as `if` block
         for _ in &[42] {
-            let foo: &Option<_> = &Some::<u8>(42);
-            if true {
+            let bar: &Option<_> = &Some::<u8>(42);
+            if bar.is_some() {
                 break;
             } else {
                 continue;
index f98e30fa376fea22a8198cb4faffd5218ba94dad..454322d8aacda727b2a8efead93c70cc554ed772 100644 (file)
@@ -5,7 +5,7 @@ LL |       } else {
    |  ____________^
 LL | |         //~ ERROR same body as `if` block
 LL | |         for _ in &[42] {
-LL | |             let foo: &Option<_> = &Some::<u8>(42);
+LL | |             let bar: &Option<_> = &Some::<u8>(42);
 ...  |
 LL | |         }
 LL | |     }
@@ -19,7 +19,7 @@ LL |       if true {
    |  _____________^
 LL | |         for _ in &[42] {
 LL | |             let foo: &Option<_> = &Some::<u8>(42);
-LL | |             if true {
+LL | |             if foo.is_some() {
 ...  |
 LL | |         }
 LL | |     } else {
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
new file mode 100644 (file)
index 0000000..8d9c311
--- /dev/null
@@ -0,0 +1,61 @@
+// run-rustfix
+// edition:2018
+#![warn(clippy::inconsistent_struct_constructor)]
+#![allow(clippy::redundant_field_names)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::no_effect)]
+#![allow(dead_code)]
+
+#[derive(Default)]
+struct Foo {
+    x: i32,
+    y: i32,
+    z: i32,
+}
+
+mod without_base {
+    use super::Foo;
+
+    fn test() {
+        let x = 1;
+        let y = 1;
+        let z = 1;
+
+        // Should lint.
+        Foo { x, y, z };
+
+        // Shoule NOT lint because the order is the same as in the definition.
+        Foo { x, y, z };
+
+        // Should NOT lint because z is not a shorthand init.
+        Foo { y, x, z: z };
+    }
+}
+
+mod with_base {
+    use super::Foo;
+
+    fn test() {
+        let x = 1;
+        let z = 1;
+
+        // Should lint.
+        Foo { x, z, ..Default::default() };
+
+        // Should NOT lint because the order is consistent with the definition.
+        Foo {
+            x,
+            z,
+            ..Default::default()
+        };
+
+        // Should NOT lint because z is not a shorthand init.
+        Foo {
+            z: z,
+            x,
+            ..Default::default()
+        };
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
new file mode 100644 (file)
index 0000000..63fac91
--- /dev/null
@@ -0,0 +1,65 @@
+// run-rustfix
+// edition:2018
+#![warn(clippy::inconsistent_struct_constructor)]
+#![allow(clippy::redundant_field_names)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::no_effect)]
+#![allow(dead_code)]
+
+#[derive(Default)]
+struct Foo {
+    x: i32,
+    y: i32,
+    z: i32,
+}
+
+mod without_base {
+    use super::Foo;
+
+    fn test() {
+        let x = 1;
+        let y = 1;
+        let z = 1;
+
+        // Should lint.
+        Foo { y, x, z };
+
+        // Shoule NOT lint because the order is the same as in the definition.
+        Foo { x, y, z };
+
+        // Should NOT lint because z is not a shorthand init.
+        Foo { y, x, z: z };
+    }
+}
+
+mod with_base {
+    use super::Foo;
+
+    fn test() {
+        let x = 1;
+        let z = 1;
+
+        // Should lint.
+        Foo {
+            z,
+            x,
+            ..Default::default()
+        };
+
+        // Should NOT lint because the order is consistent with the definition.
+        Foo {
+            x,
+            z,
+            ..Default::default()
+        };
+
+        // Should NOT lint because z is not a shorthand init.
+        Foo {
+            z: z,
+            x,
+            ..Default::default()
+        };
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr
new file mode 100644 (file)
index 0000000..d7abe44
--- /dev/null
@@ -0,0 +1,20 @@
+error: inconsistent struct constructor
+  --> $DIR/inconsistent_struct_constructor.rs:25:9
+   |
+LL |         Foo { y, x, z };
+   |         ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }`
+   |
+   = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings`
+
+error: inconsistent struct constructor
+  --> $DIR/inconsistent_struct_constructor.rs:43:9
+   |
+LL | /         Foo {
+LL | |             z,
+LL | |             x,
+LL | |             ..Default::default()
+LL | |         };
+   | |_________^ help: try: `Foo { x, z, ..Default::default() }`
+
+error: aborting due to 2 previous errors
+
index e6cf337d1bb1baa932b13f3665cdc8d7764395ee..6e65fdbd04e7dafc7acfae04f81d757218b2bb9c 100644 (file)
@@ -14,6 +14,7 @@ trait FalsePositive {
 struct D;
 struct E;
 struct F;
+struct G;
 
 impl A {
     // Should be detected; emit warning
@@ -73,6 +74,13 @@ fn to_string(&self, _i: i32) -> String {
     }
 }
 
+impl G {
+    // Should not be detected, as it does not match the function signature
+    fn to_string<const _N: usize>(&self) -> String {
+        "G.to_string()".to_string()
+    }
+}
+
 fn main() {
     let a = A;
     a.to_string();
@@ -93,4 +101,7 @@ fn main() {
 
     let f = F;
     f.to_string(1);
+
+    let g = G;
+    g.to_string::<1>();
 }
index 4f331f5bec9e6fe87073d1cdb63991862db4bf28..f5fcc193b4d8a2332af013451b679751b0629d68 100644 (file)
@@ -1,5 +1,5 @@
 error: implementation of inherent method `to_string(&self) -> String` for type `A`
-  --> $DIR/inherent_to_string.rs:20:5
+  --> $DIR/inherent_to_string.rs:21:5
    |
 LL | /     fn to_string(&self) -> String {
 LL | |         "A.to_string()".to_string()
@@ -10,7 +10,7 @@ LL | |     }
    = help: implement trait `Display` for type `A` instead
 
 error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`
-  --> $DIR/inherent_to_string.rs:44:5
+  --> $DIR/inherent_to_string.rs:45:5
    |
 LL | /     fn to_string(&self) -> String {
 LL | |         "C.to_string()".to_string()
diff --git a/src/tools/clippy/tests/ui/manual_map_option.fixed b/src/tools/clippy/tests/ui/manual_map_option.fixed
new file mode 100644 (file)
index 0000000..1935090
--- /dev/null
@@ -0,0 +1,70 @@
+// run-rustfix
+
+#![warn(clippy::manual_map)]
+#![allow(clippy::no_effect, clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats)]
+
+fn main() {
+    Some(0).map(|_| 2);
+
+    Some(0).map(|x| x + 1);
+
+    Some("").map(|x| x.is_empty());
+
+    Some(0).map(|x| !x);
+
+    #[rustfmt::skip]
+    Some(0).map(std::convert::identity);
+
+    Some(&String::new()).map(|x| str::len(x));
+
+    match Some(0) {
+        Some(x) if false => Some(x + 1),
+        _ => None,
+    };
+
+    Some([0, 1]).as_ref().map(|x| x[0]);
+
+    Some(0).map(|x| x * 2);
+
+    Some(String::new()).as_ref().map(|x| x.is_empty());
+
+    Some(String::new()).as_ref().map(|x| x.len());
+
+    Some(0).map(|x| x + x);
+
+    #[warn(clippy::option_map_unit_fn)]
+    match &mut Some(String::new()) {
+        Some(x) => Some(x.push_str("")),
+        None => None,
+    };
+
+    #[allow(clippy::option_map_unit_fn)]
+    {
+        Some(String::new()).as_mut().map(|x| x.push_str(""));
+    }
+
+    Some(String::new()).as_ref().map(|x| x.len());
+
+    Some(String::new()).as_ref().map(|x| x.is_empty());
+
+    Some((0, 1, 2)).map(|(x, y, z)| x + y + z);
+
+    Some([1, 2, 3]).map(|[first, ..]| first);
+
+    Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x));
+
+    match Some((String::new(), 0)) {
+        Some((ref x, y)) => Some((y, x)),
+        None => None,
+    };
+
+    match Some(Some(0)) {
+        Some(Some(_)) | Some(None) => Some(0),
+        None => None,
+    };
+
+    match Some(Some((0, 1))) {
+        Some(Some((x, 1))) => Some(x),
+        _ => None,
+    };
+}
diff --git a/src/tools/clippy/tests/ui/manual_map_option.rs b/src/tools/clippy/tests/ui/manual_map_option.rs
new file mode 100644 (file)
index 0000000..8b8187d
--- /dev/null
@@ -0,0 +1,122 @@
+// run-rustfix
+
+#![warn(clippy::manual_map)]
+#![allow(clippy::no_effect, clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats)]
+
+fn main() {
+    match Some(0) {
+        Some(_) => Some(2),
+        None::<u32> => None,
+    };
+
+    match Some(0) {
+        Some(x) => Some(x + 1),
+        _ => None,
+    };
+
+    match Some("") {
+        Some(x) => Some(x.is_empty()),
+        None => None,
+    };
+
+    if let Some(x) = Some(0) {
+        Some(!x)
+    } else {
+        None
+    };
+
+    #[rustfmt::skip]
+    match Some(0) {
+        Some(x) => { Some(std::convert::identity(x)) }
+        None => { None }
+    };
+
+    match Some(&String::new()) {
+        Some(x) => Some(str::len(x)),
+        None => None,
+    };
+
+    match Some(0) {
+        Some(x) if false => Some(x + 1),
+        _ => None,
+    };
+
+    match &Some([0, 1]) {
+        Some(x) => Some(x[0]),
+        &None => None,
+    };
+
+    match &Some(0) {
+        &Some(x) => Some(x * 2),
+        None => None,
+    };
+
+    match Some(String::new()) {
+        Some(ref x) => Some(x.is_empty()),
+        _ => None,
+    };
+
+    match &&Some(String::new()) {
+        Some(x) => Some(x.len()),
+        _ => None,
+    };
+
+    match &&Some(0) {
+        &&Some(x) => Some(x + x),
+        &&_ => None,
+    };
+
+    #[warn(clippy::option_map_unit_fn)]
+    match &mut Some(String::new()) {
+        Some(x) => Some(x.push_str("")),
+        None => None,
+    };
+
+    #[allow(clippy::option_map_unit_fn)]
+    {
+        match &mut Some(String::new()) {
+            Some(x) => Some(x.push_str("")),
+            None => None,
+        };
+    }
+
+    match &mut Some(String::new()) {
+        Some(ref x) => Some(x.len()),
+        None => None,
+    };
+
+    match &mut &Some(String::new()) {
+        Some(x) => Some(x.is_empty()),
+        &mut _ => None,
+    };
+
+    match Some((0, 1, 2)) {
+        Some((x, y, z)) => Some(x + y + z),
+        None => None,
+    };
+
+    match Some([1, 2, 3]) {
+        Some([first, ..]) => Some(first),
+        None => None,
+    };
+
+    match &Some((String::new(), "test")) {
+        Some((x, y)) => Some((y, x)),
+        None => None,
+    };
+
+    match Some((String::new(), 0)) {
+        Some((ref x, y)) => Some((y, x)),
+        None => None,
+    };
+
+    match Some(Some(0)) {
+        Some(Some(_)) | Some(None) => Some(0),
+        None => None,
+    };
+
+    match Some(Some((0, 1))) {
+        Some(Some((x, 1))) => Some(x),
+        _ => None,
+    };
+}
diff --git a/src/tools/clippy/tests/ui/manual_map_option.stderr b/src/tools/clippy/tests/ui/manual_map_option.stderr
new file mode 100644 (file)
index 0000000..210a30d
--- /dev/null
@@ -0,0 +1,158 @@
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:7:5
+   |
+LL | /     match Some(0) {
+LL | |         Some(_) => Some(2),
+LL | |         None::<u32> => None,
+LL | |     };
+   | |_____^ help: try this: `Some(0).map(|_| 2)`
+   |
+   = note: `-D clippy::manual-map` implied by `-D warnings`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:12:5
+   |
+LL | /     match Some(0) {
+LL | |         Some(x) => Some(x + 1),
+LL | |         _ => None,
+LL | |     };
+   | |_____^ help: try this: `Some(0).map(|x| x + 1)`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:17:5
+   |
+LL | /     match Some("") {
+LL | |         Some(x) => Some(x.is_empty()),
+LL | |         None => None,
+LL | |     };
+   | |_____^ help: try this: `Some("").map(|x| x.is_empty())`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:22:5
+   |
+LL | /     if let Some(x) = Some(0) {
+LL | |         Some(!x)
+LL | |     } else {
+LL | |         None
+LL | |     };
+   | |_____^ help: try this: `Some(0).map(|x| !x)`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:29:5
+   |
+LL | /     match Some(0) {
+LL | |         Some(x) => { Some(std::convert::identity(x)) }
+LL | |         None => { None }
+LL | |     };
+   | |_____^ help: try this: `Some(0).map(std::convert::identity)`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:34:5
+   |
+LL | /     match Some(&String::new()) {
+LL | |         Some(x) => Some(str::len(x)),
+LL | |         None => None,
+LL | |     };
+   | |_____^ help: try this: `Some(&String::new()).map(|x| str::len(x))`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:44:5
+   |
+LL | /     match &Some([0, 1]) {
+LL | |         Some(x) => Some(x[0]),
+LL | |         &None => None,
+LL | |     };
+   | |_____^ help: try this: `Some([0, 1]).as_ref().map(|x| x[0])`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:49:5
+   |
+LL | /     match &Some(0) {
+LL | |         &Some(x) => Some(x * 2),
+LL | |         None => None,
+LL | |     };
+   | |_____^ help: try this: `Some(0).map(|x| x * 2)`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:54:5
+   |
+LL | /     match Some(String::new()) {
+LL | |         Some(ref x) => Some(x.is_empty()),
+LL | |         _ => None,
+LL | |     };
+   | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:59:5
+   |
+LL | /     match &&Some(String::new()) {
+LL | |         Some(x) => Some(x.len()),
+LL | |         _ => None,
+LL | |     };
+   | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:64:5
+   |
+LL | /     match &&Some(0) {
+LL | |         &&Some(x) => Some(x + x),
+LL | |         &&_ => None,
+LL | |     };
+   | |_____^ help: try this: `Some(0).map(|x| x + x)`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:77:9
+   |
+LL | /         match &mut Some(String::new()) {
+LL | |             Some(x) => Some(x.push_str("")),
+LL | |             None => None,
+LL | |         };
+   | |_________^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:83:5
+   |
+LL | /     match &mut Some(String::new()) {
+LL | |         Some(ref x) => Some(x.len()),
+LL | |         None => None,
+LL | |     };
+   | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:88:5
+   |
+LL | /     match &mut &Some(String::new()) {
+LL | |         Some(x) => Some(x.is_empty()),
+LL | |         &mut _ => None,
+LL | |     };
+   | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:93:5
+   |
+LL | /     match Some((0, 1, 2)) {
+LL | |         Some((x, y, z)) => Some(x + y + z),
+LL | |         None => None,
+LL | |     };
+   | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:98:5
+   |
+LL | /     match Some([1, 2, 3]) {
+LL | |         Some([first, ..]) => Some(first),
+LL | |         None => None,
+LL | |     };
+   | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:103:5
+   |
+LL | /     match &Some((String::new(), "test")) {
+LL | |         Some((x, y)) => Some((y, x)),
+LL | |         None => None,
+LL | |     };
+   | |_____^ help: try this: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))`
+
+error: aborting due to 17 previous errors
+
index 5e57c752b5a034fbe5d2a80588938068a54b384e..a4ec803024edf68689d732abaf090ac9ec21fa87 100644 (file)
@@ -1,6 +1,4 @@
-#![allow(clippy::unnecessary_wraps)]
-#[warn(clippy::result_unit_err)]
-#[allow(unused)]
+#![warn(clippy::result_unit_err)]
 
 pub fn returns_unit_error() -> Result<u32, ()> {
     Err(())
@@ -36,4 +34,23 @@ pub fn unit_error(&self) -> Result<usize, ()> {
     }
 }
 
+// https://github.com/rust-lang/rust-clippy/issues/6546
+pub mod issue_6546 {
+    type ResInv<A, B> = Result<B, A>;
+
+    pub fn should_lint() -> ResInv<(), usize> {
+        Ok(0)
+    }
+
+    pub fn should_not_lint() -> ResInv<usize, ()> {
+        Ok(())
+    }
+
+    type MyRes<A, B> = Result<(A, B), Box<dyn std::error::Error>>;
+
+    pub fn should_not_lint2(x: i32) -> MyRes<i32, ()> {
+        Ok((x, ()))
+    }
+}
+
 fn main() {}
index 12901b354f9169d76fdf184a1e27934b6a38d011..41d8b0a7cb7f82e92989fb56c909a32b8e27315c 100644 (file)
@@ -1,5 +1,5 @@
 error: this returns a `Result<_, ()>
-  --> $DIR/result_unit_error.rs:5:1
+  --> $DIR/result_unit_error.rs:3:1
    |
 LL | pub fn returns_unit_error() -> Result<u32, ()> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | pub fn returns_unit_error() -> Result<u32, ()> {
    = help: use a custom Error type instead
 
 error: this returns a `Result<_, ()>
-  --> $DIR/result_unit_error.rs:14:5
+  --> $DIR/result_unit_error.rs:12:5
    |
 LL |     fn get_that_error(&self) -> Result<bool, ()>;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL |     fn get_that_error(&self) -> Result<bool, ()>;
    = help: use a custom Error type instead
 
 error: this returns a `Result<_, ()>
-  --> $DIR/result_unit_error.rs:16:5
+  --> $DIR/result_unit_error.rs:14:5
    |
 LL |     fn get_this_one_too(&self) -> Result<bool, ()> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -24,12 +24,20 @@ LL |     fn get_this_one_too(&self) -> Result<bool, ()> {
    = help: use a custom Error type instead
 
 error: this returns a `Result<_, ()>
-  --> $DIR/result_unit_error.rs:34:5
+  --> $DIR/result_unit_error.rs:32:5
    |
 LL |     pub fn unit_error(&self) -> Result<usize, ()> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: use a custom Error type instead
 
-error: aborting due to 4 previous errors
+error: this returns a `Result<_, ()>
+  --> $DIR/result_unit_error.rs:41:5
+   |
+LL |     pub fn should_lint() -> ResInv<(), usize> {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use a custom Error type instead
+
+error: aborting due to 5 previous errors
 
index a4570098d7167da8393f6c4933d20cf277be8962..a510263e67da19a2256975ffe18767606dffae0c 100644 (file)
@@ -116,8 +116,53 @@ fn issue_6384(s: &str) -> Option<&str> {
     })
 }
 
+// should be linted
+fn issue_6640_1(a: bool, b: bool) -> Option<()> {
+    if a && b {
+        return Some(());
+    }
+    if a {
+        Some(());
+        Some(())
+    } else {
+        return Some(());
+    }
+}
+
+// should be linted
+fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> {
+    if a && b {
+        return Ok(());
+    }
+    if a {
+        Ok(())
+    } else {
+        return Ok(());
+    }
+}
+
+// should not be linted
+fn issue_6640_3() -> Option<()> {
+    if true {
+        Some(())
+    } else {
+        None
+    }
+}
+
+// should not be linted
+fn issue_6640_4() -> Result<(), ()> {
+    if true {
+        Ok(())
+    } else {
+        Err(())
+    }
+}
+
 fn main() {
     // method calls are not linted
     func1(true, true);
     func2(true, true);
+    issue_6640_1(true, true);
+    issue_6640_2(true, true);
 }
index 410f054b8efcaf8c85536471e4bd08eaa5233701..9a861c61a4679bd4fc7f46f83dca2a84a895af55 100644 (file)
@@ -15,7 +15,7 @@ help: remove `Option` from the return type...
    |
 LL | fn func1(a: bool, b: bool) -> i32 {
    |                               ^^^
-help: ...and change the returning expressions
+help: ...and then change returning expressions
    |
 LL |         return 42;
 LL |     }
@@ -41,7 +41,7 @@ help: remove `Option` from the return type...
    |
 LL | fn func2(a: bool, b: bool) -> i32 {
    |                               ^^^
-help: ...and change the returning expressions
+help: ...and then change returning expressions
    |
 LL |         return 10;
 LL |     }
@@ -63,7 +63,7 @@ help: remove `Option` from the return type...
    |
 LL | fn func5() -> i32 {
    |               ^^^
-help: ...and change the returning expressions
+help: ...and then change returning expressions
    |
 LL |     1
    |
@@ -80,7 +80,7 @@ help: remove `Result` from the return type...
    |
 LL | fn func7() -> i32 {
    |               ^^^
-help: ...and change the returning expressions
+help: ...and then change returning expressions
    |
 LL |     1
    |
@@ -97,10 +97,62 @@ help: remove `Option` from the return type...
    |
 LL |     fn func12() -> i32 {
    |                    ^^^
-help: ...and change the returning expressions
+help: ...and then change returning expressions
    |
 LL |         1
    |
 
-error: aborting due to 5 previous errors
+error: this function's return value is unnecessary
+  --> $DIR/unnecessary_wraps.rs:120:1
+   |
+LL | / fn issue_6640_1(a: bool, b: bool) -> Option<()> {
+LL | |     if a && b {
+LL | |         return Some(());
+LL | |     }
+...  |
+LL | |     }
+LL | | }
+   | |_^
+   |
+help: remove the return type...
+   |
+LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> {
+   |                                      ^^^^^^^^^^
+help: ...and then remove returned values
+   |
+LL |         return ;
+LL |     }
+LL |     if a {
+LL |         Some(());
+LL |         
+LL |     } else {
+ ...
+
+error: this function's return value is unnecessary
+  --> $DIR/unnecessary_wraps.rs:133:1
+   |
+LL | / fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> {
+LL | |     if a && b {
+LL | |         return Ok(());
+LL | |     }
+...  |
+LL | |     }
+LL | | }
+   | |_^
+   |
+help: remove the return type...
+   |
+LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> {
+   |                                      ^^^^^^^^^^^^^^^
+help: ...and then remove returned values
+   |
+LL |         return ;
+LL |     }
+LL |     if a {
+LL |         
+LL |     } else {
+LL |         return ;
+   |
+
+error: aborting due to 7 previous errors
 
index af0b577634863b26356d271f9cd0d0c04545290a..fdf8905f812f6fbc1fa587f9aad0fc5116fa893d 100644 (file)
@@ -1,11 +1,11 @@
 #![warn(clippy::upper_case_acronyms)]
 
-struct HTTPResponse; // linted
+struct HTTPResponse; // not linted by default, but with cfg option
 
 struct CString; // not linted
 
 enum Flags {
-    NS, // linted
+    NS, // not linted
     CWR,
     ECE,
     URG,
@@ -16,6 +16,7 @@ enum Flags {
     FIN,
 }
 
-struct GCCLLVMSomething; // linted, beware that lint suggests `GccllvmSomething` instead of `GccLlvmSomething`
+struct GCCLLVMSomething; // linted with cfg option, beware that lint suggests `GccllvmSomething` instead of
+                         // `GccLlvmSomething`
 
 fn main() {}
index 2065fe10bb151cdcdf905df38f9a7d7ddbaaab13..bbe38991e52714a89faff46fd844bbc4a8adb346 100644 (file)
@@ -1,22 +1,10 @@
-error: name `HTTPResponse` contains a capitalized acronym
-  --> $DIR/upper_case_acronyms.rs:3:8
-   |
-LL | struct HTTPResponse; // linted
-   |        ^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `HttpResponse`
-   |
-   = note: `-D clippy::upper-case-acronyms` implied by `-D warnings`
-
-error: name `NS` contains a capitalized acronym
-  --> $DIR/upper_case_acronyms.rs:8:5
-   |
-LL |     NS, // linted
-   |     ^^ help: consider making the acronym lowercase, except the initial letter (notice the capitalization): `Ns`
-
 error: name `CWR` contains a capitalized acronym
   --> $DIR/upper_case_acronyms.rs:9:5
    |
 LL |     CWR,
    |     ^^^ help: consider making the acronym lowercase, except the initial letter: `Cwr`
+   |
+   = note: `-D clippy::upper-case-acronyms` implied by `-D warnings`
 
 error: name `ECE` contains a capitalized acronym
   --> $DIR/upper_case_acronyms.rs:10:5
@@ -60,11 +48,5 @@ error: name `FIN` contains a capitalized acronym
 LL |     FIN,
    |     ^^^ help: consider making the acronym lowercase, except the initial letter: `Fin`
 
-error: name `GCCLLVMSomething` contains a capitalized acronym
-  --> $DIR/upper_case_acronyms.rs:19:8
-   |
-LL | struct GCCLLVMSomething; // linted, beware that lint suggests `GccllvmSomething` instead of `GccLlvmSomething`
-   |        ^^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `GccllvmSomething`
-
-error: aborting due to 11 previous errors
+error: aborting due to 8 previous errors
 
index bb2012441d90c591cbcb1d21e61fb5f5cc896a9b..95e7bc754310f8393cf59cf6939ddc2f8e7bf170 100644 (file)
@@ -1,9 +1,13 @@
 // run-rustfix
 // edition:2018
+// aux-build:proc_macro_derive.rs
 
 #![warn(clippy::use_self)]
 #![allow(dead_code)]
-#![allow(clippy::should_implement_trait, clippy::upper_case_acronyms)]
+#![allow(clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into)]
+
+#[macro_use]
+extern crate proc_macro_derive;
 
 fn main() {}
 
@@ -71,13 +75,12 @@ mod lifetimes {
 
 mod issue2894 {
     trait IntoBytes {
-        #[allow(clippy::wrong_self_convention)]
-        fn into_bytes(&self) -> Vec<u8>;
+        fn to_bytes(&self) -> Vec<u8>;
     }
 
     // This should not be linted
     impl IntoBytes for u8 {
-        fn into_bytes(&self) -> Vec<u8> {
+        fn to_bytes(&self) -> Vec<u8> {
             vec![*self]
         }
     }
@@ -110,8 +113,8 @@ mod tuple_structs {
 mod macros {
     macro_rules! use_self_expand {
         () => {
-            fn new() -> Self {
-                Self {}
+            fn new() -> Foo {
+                Foo {}
             }
         };
     }
@@ -119,8 +122,11 @@ mod macros {
     struct Foo {}
 
     impl Foo {
-        use_self_expand!(); // Should lint in local macros
+        use_self_expand!(); // Should not lint in local macros
     }
+
+    #[derive(StructAUseSelf)] // Should not lint in derives
+    struct A;
 }
 
 mod nesting {
@@ -177,11 +183,22 @@ mod issue3410 {
     struct B;
 
     trait Trait<T> {
-        fn a(v: T);
+        fn a(v: T) -> Self;
     }
 
     impl Trait<Vec<A>> for Vec<B> {
-        fn a(_: Vec<A>) {}
+        fn a(_: Vec<A>) -> Self {
+            unimplemented!()
+        }
+    }
+
+    impl<T> Trait<Vec<A>> for Vec<T>
+    where
+        T: Trait<B>,
+    {
+        fn a(v: Vec<A>) -> Self {
+            <Vec<B>>::a(v).into_iter().map(Trait::a).collect()
+        }
     }
 }
 
@@ -252,3 +269,192 @@ mod paths_created_by_lowering {
         }
     }
 }
+
+// reused from #1997
+mod generics {
+    struct Foo<T> {
+        value: T,
+    }
+
+    impl<T> Foo<T> {
+        // `Self` is applicable here
+        fn foo(value: T) -> Self {
+            Self { value }
+        }
+
+        // `Cannot` use `Self` as a return type as the generic types are different
+        fn bar(value: i32) -> Foo<i32> {
+            Foo { value }
+        }
+    }
+}
+
+mod issue4140 {
+    pub struct Error<From, To> {
+        _from: From,
+        _too: To,
+    }
+
+    pub trait From<T> {
+        type From;
+        type To;
+
+        fn from(value: T) -> Self;
+    }
+
+    pub trait TryFrom<T>
+    where
+        Self: Sized,
+    {
+        type From;
+        type To;
+
+        fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>;
+    }
+
+    impl<F, T> TryFrom<F> for T
+    where
+        T: From<F>,
+    {
+        type From = Self;
+        type To = Self;
+
+        fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> {
+            Ok(From::from(value))
+        }
+    }
+
+    impl From<bool> for i64 {
+        type From = bool;
+        type To = Self;
+
+        fn from(value: bool) -> Self {
+            if value {
+                100
+            } else {
+                0
+            }
+        }
+    }
+}
+
+mod issue2843 {
+    trait Foo {
+        type Bar;
+    }
+
+    impl Foo for usize {
+        type Bar = u8;
+    }
+
+    impl<T: Foo> Foo for Option<T> {
+        type Bar = Option<T::Bar>;
+    }
+}
+
+mod issue3859 {
+    pub struct Foo;
+    pub struct Bar([usize; 3]);
+
+    impl Foo {
+        pub const BAR: usize = 3;
+
+        pub fn foo() {
+            const _X: usize = Foo::BAR;
+            // const _Y: usize = Self::BAR;
+        }
+    }
+}
+
+mod issue4305 {
+    trait Foo: 'static {}
+
+    struct Bar;
+
+    impl Foo for Bar {}
+
+    impl<T: Foo> From<T> for Box<dyn Foo> {
+        fn from(t: T) -> Self {
+            Box::new(t)
+        }
+    }
+}
+
+mod lint_at_item_level {
+    struct Foo {}
+
+    #[allow(clippy::use_self)]
+    impl Foo {
+        fn new() -> Foo {
+            Foo {}
+        }
+    }
+
+    #[allow(clippy::use_self)]
+    impl Default for Foo {
+        fn default() -> Foo {
+            Foo::new()
+        }
+    }
+}
+
+mod lint_at_impl_item_level {
+    struct Foo {}
+
+    impl Foo {
+        #[allow(clippy::use_self)]
+        fn new() -> Foo {
+            Foo {}
+        }
+    }
+
+    impl Default for Foo {
+        #[allow(clippy::use_self)]
+        fn default() -> Foo {
+            Foo::new()
+        }
+    }
+}
+
+mod issue4734 {
+    #[repr(C, packed)]
+    pub struct X {
+        pub x: u32,
+    }
+
+    impl From<X> for u32 {
+        fn from(c: X) -> Self {
+            unsafe { core::mem::transmute(c) }
+        }
+    }
+}
+
+mod nested_paths {
+    use std::convert::Into;
+    mod submod {
+        pub struct B {}
+        pub struct C {}
+
+        impl Into<C> for B {
+            fn into(self) -> C {
+                C {}
+            }
+        }
+    }
+
+    struct A<T> {
+        t: T,
+    }
+
+    impl<T> A<T> {
+        fn new<V: Into<T>>(v: V) -> Self {
+            Self { t: Into::into(v) }
+        }
+    }
+
+    impl A<submod::C> {
+        fn test() -> Self {
+            Self::new::<submod::B>(submod::B {})
+        }
+    }
+}
index ddfd2beba31078a9c2b2e36a3a74699965e5bb47..75424f341597d1e50aa08ef0362dfeb9b8d1256c 100644 (file)
@@ -1,9 +1,13 @@
 // run-rustfix
 // edition:2018
+// aux-build:proc_macro_derive.rs
 
 #![warn(clippy::use_self)]
 #![allow(dead_code)]
-#![allow(clippy::should_implement_trait, clippy::upper_case_acronyms)]
+#![allow(clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into)]
+
+#[macro_use]
+extern crate proc_macro_derive;
 
 fn main() {}
 
@@ -71,13 +75,12 @@ fn clone(&self) -> Foo<'a> {
 
 mod issue2894 {
     trait IntoBytes {
-        #[allow(clippy::wrong_self_convention)]
-        fn into_bytes(&self) -> Vec<u8>;
+        fn to_bytes(&self) -> Vec<u8>;
     }
 
     // This should not be linted
     impl IntoBytes for u8 {
-        fn into_bytes(&self) -> Vec<u8> {
+        fn to_bytes(&self) -> Vec<u8> {
             vec![*self]
         }
     }
@@ -87,7 +90,7 @@ mod existential {
     struct Foo;
 
     impl Foo {
-        fn bad(foos: &[Self]) -> impl Iterator<Item = &Foo> {
+        fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
             foos.iter()
         }
 
@@ -119,8 +122,11 @@ fn new() -> Foo {
     struct Foo {}
 
     impl Foo {
-        use_self_expand!(); // Should lint in local macros
+        use_self_expand!(); // Should not lint in local macros
     }
+
+    #[derive(StructAUseSelf)] // Should not lint in derives
+    struct A;
 }
 
 mod nesting {
@@ -177,11 +183,22 @@ mod issue3410 {
     struct B;
 
     trait Trait<T> {
-        fn a(v: T);
+        fn a(v: T) -> Self;
     }
 
     impl Trait<Vec<A>> for Vec<B> {
-        fn a(_: Vec<A>) {}
+        fn a(_: Vec<A>) -> Self {
+            unimplemented!()
+        }
+    }
+
+    impl<T> Trait<Vec<A>> for Vec<T>
+    where
+        T: Trait<B>,
+    {
+        fn a(v: Vec<A>) -> Self {
+            <Vec<B>>::a(v).into_iter().map(Trait::a).collect()
+        }
     }
 }
 
@@ -252,3 +269,192 @@ fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
         }
     }
 }
+
+// reused from #1997
+mod generics {
+    struct Foo<T> {
+        value: T,
+    }
+
+    impl<T> Foo<T> {
+        // `Self` is applicable here
+        fn foo(value: T) -> Foo<T> {
+            Foo { value }
+        }
+
+        // `Cannot` use `Self` as a return type as the generic types are different
+        fn bar(value: i32) -> Foo<i32> {
+            Foo { value }
+        }
+    }
+}
+
+mod issue4140 {
+    pub struct Error<From, To> {
+        _from: From,
+        _too: To,
+    }
+
+    pub trait From<T> {
+        type From;
+        type To;
+
+        fn from(value: T) -> Self;
+    }
+
+    pub trait TryFrom<T>
+    where
+        Self: Sized,
+    {
+        type From;
+        type To;
+
+        fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>;
+    }
+
+    impl<F, T> TryFrom<F> for T
+    where
+        T: From<F>,
+    {
+        type From = T::From;
+        type To = T::To;
+
+        fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> {
+            Ok(From::from(value))
+        }
+    }
+
+    impl From<bool> for i64 {
+        type From = bool;
+        type To = Self;
+
+        fn from(value: bool) -> Self {
+            if value {
+                100
+            } else {
+                0
+            }
+        }
+    }
+}
+
+mod issue2843 {
+    trait Foo {
+        type Bar;
+    }
+
+    impl Foo for usize {
+        type Bar = u8;
+    }
+
+    impl<T: Foo> Foo for Option<T> {
+        type Bar = Option<T::Bar>;
+    }
+}
+
+mod issue3859 {
+    pub struct Foo;
+    pub struct Bar([usize; 3]);
+
+    impl Foo {
+        pub const BAR: usize = 3;
+
+        pub fn foo() {
+            const _X: usize = Foo::BAR;
+            // const _Y: usize = Self::BAR;
+        }
+    }
+}
+
+mod issue4305 {
+    trait Foo: 'static {}
+
+    struct Bar;
+
+    impl Foo for Bar {}
+
+    impl<T: Foo> From<T> for Box<dyn Foo> {
+        fn from(t: T) -> Self {
+            Box::new(t)
+        }
+    }
+}
+
+mod lint_at_item_level {
+    struct Foo {}
+
+    #[allow(clippy::use_self)]
+    impl Foo {
+        fn new() -> Foo {
+            Foo {}
+        }
+    }
+
+    #[allow(clippy::use_self)]
+    impl Default for Foo {
+        fn default() -> Foo {
+            Foo::new()
+        }
+    }
+}
+
+mod lint_at_impl_item_level {
+    struct Foo {}
+
+    impl Foo {
+        #[allow(clippy::use_self)]
+        fn new() -> Foo {
+            Foo {}
+        }
+    }
+
+    impl Default for Foo {
+        #[allow(clippy::use_self)]
+        fn default() -> Foo {
+            Foo::new()
+        }
+    }
+}
+
+mod issue4734 {
+    #[repr(C, packed)]
+    pub struct X {
+        pub x: u32,
+    }
+
+    impl From<X> for u32 {
+        fn from(c: X) -> Self {
+            unsafe { core::mem::transmute(c) }
+        }
+    }
+}
+
+mod nested_paths {
+    use std::convert::Into;
+    mod submod {
+        pub struct B {}
+        pub struct C {}
+
+        impl Into<C> for B {
+            fn into(self) -> C {
+                C {}
+            }
+        }
+    }
+
+    struct A<T> {
+        t: T,
+    }
+
+    impl<T> A<T> {
+        fn new<V: Into<T>>(v: V) -> Self {
+            Self { t: Into::into(v) }
+        }
+    }
+
+    impl A<submod::C> {
+        fn test() -> Self {
+            A::new::<submod::B>(submod::B {})
+        }
+    }
+}
index 80e1bfc75e80a379dac65b82a5b304d802eeefa2..37dfef7cfe0e516996b23ddc5d43c5393a4004d6 100644 (file)
@@ -1,5 +1,5 @@
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:14:21
+  --> $DIR/use_self.rs:18:21
    |
 LL |         fn new() -> Foo {
    |                     ^^^ help: use the applicable keyword: `Self`
@@ -7,158 +7,172 @@ LL |         fn new() -> Foo {
    = note: `-D clippy::use-self` implied by `-D warnings`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:15:13
+  --> $DIR/use_self.rs:19:13
    |
 LL |             Foo {}
    |             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:17:22
+  --> $DIR/use_self.rs:21:22
    |
 LL |         fn test() -> Foo {
    |                      ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:18:13
+  --> $DIR/use_self.rs:22:13
    |
 LL |             Foo::new()
    |             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:23:25
+  --> $DIR/use_self.rs:27:25
    |
 LL |         fn default() -> Foo {
    |                         ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:24:13
+  --> $DIR/use_self.rs:28:13
    |
 LL |             Foo::new()
    |             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:90:56
+  --> $DIR/use_self.rs:93:24
    |
-LL |         fn bad(foos: &[Self]) -> impl Iterator<Item = &Foo> {
-   |                                                        ^^^ help: use the applicable keyword: `Self`
+LL |         fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
+   |                        ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:105:13
+  --> $DIR/use_self.rs:93:55
+   |
+LL |         fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
+   |                                                       ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+  --> $DIR/use_self.rs:108:13
    |
 LL |             TS(0)
    |             ^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:113:25
-   |
-LL |             fn new() -> Foo {
-   |                         ^^^ help: use the applicable keyword: `Self`
-...
-LL |         use_self_expand!(); // Should lint in local macros
-   |         ------------------- in this macro invocation
+  --> $DIR/use_self.rs:143:29
    |
-   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+LL |                 fn bar() -> Bar {
+   |                             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:114:17
-   |
-LL |                 Foo {}
-   |                 ^^^ help: use the applicable keyword: `Self`
-...
-LL |         use_self_expand!(); // Should lint in local macros
-   |         ------------------- in this macro invocation
+  --> $DIR/use_self.rs:144:21
    |
-   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+LL |                     Bar { foo: Foo {} }
+   |                     ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:149:21
+  --> $DIR/use_self.rs:155:21
    |
 LL |         fn baz() -> Foo {
    |                     ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:150:13
+  --> $DIR/use_self.rs:156:13
    |
 LL |             Foo {}
    |             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:137:29
-   |
-LL |                 fn bar() -> Bar {
-   |                             ^^^ help: use the applicable keyword: `Self`
-
-error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:138:21
-   |
-LL |                     Bar { foo: Foo {} }
-   |                     ^^^ help: use the applicable keyword: `Self`
-
-error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:167:21
+  --> $DIR/use_self.rs:173:21
    |
 LL |             let _ = Enum::B(42);
    |                     ^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:168:21
+  --> $DIR/use_self.rs:174:21
    |
 LL |             let _ = Enum::C { field: true };
    |                     ^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:169:21
+  --> $DIR/use_self.rs:175:21
    |
 LL |             let _ = Enum::A;
    |                     ^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:200:13
+  --> $DIR/use_self.rs:217:13
    |
 LL |             nested::A::fun_1();
    |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:201:13
+  --> $DIR/use_self.rs:218:13
    |
 LL |             nested::A::A;
    |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:203:13
+  --> $DIR/use_self.rs:220:13
    |
 LL |             nested::A {};
    |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:222:13
+  --> $DIR/use_self.rs:239:13
    |
 LL |             TestStruct::from_something()
    |             ^^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:236:25
+  --> $DIR/use_self.rs:253:25
    |
 LL |         async fn g() -> S {
    |                         ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:237:13
+  --> $DIR/use_self.rs:254:13
    |
 LL |             S {}
    |             ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:241:16
+  --> $DIR/use_self.rs:258:16
    |
 LL |             &p[S::A..S::B]
    |                ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:241:22
+  --> $DIR/use_self.rs:258:22
    |
 LL |             &p[S::A..S::B]
    |                      ^ help: use the applicable keyword: `Self`
 
-error: aborting due to 25 previous errors
+error: unnecessary structure name repetition
+  --> $DIR/use_self.rs:281:29
+   |
+LL |         fn foo(value: T) -> Foo<T> {
+   |                             ^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+  --> $DIR/use_self.rs:282:13
+   |
+LL |             Foo { value }
+   |             ^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+  --> $DIR/use_self.rs:319:21
+   |
+LL |         type From = T::From;
+   |                     ^^^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+  --> $DIR/use_self.rs:320:19
+   |
+LL |         type To = T::To;
+   |                   ^^^^^ help: use the applicable keyword: `Self`
+
+error: unnecessary structure name repetition
+  --> $DIR/use_self.rs:457:13
+   |
+LL |             A::new::<submod::B>(submod::B {})
+   |             ^ help: use the applicable keyword: `Self`
+
+error: aborting due to 29 previous errors
 
index 1582ae114bf4c4b486af912d3e9d297171a49cda..9bcd692fb3511d805c24f6b49902eb31300936de 100644 (file)
@@ -47,7 +47,8 @@ impl Mul for Bad {
 
 impl Clone for Bad {
     fn clone(&self) -> Self {
-        Self
+        // FIXME: applicable here
+        Bad
     }
 }
 
index 70667b9797e762c0ec4f383d58d0a1ced2316ce3..de305d40f330b1a4707995a0f286a844b6da0449 100644 (file)
@@ -47,6 +47,7 @@ fn mul(self, rhs: Bad) -> Bad {
 
 impl Clone for Bad {
     fn clone(&self) -> Self {
+        // FIXME: applicable here
         Bad
     }
 }
index 4f2506cc1192fecd9514b2dcb36ce070f954642c..55af3ff2a93d9af515ccfea3db0cf2afd2a0be11 100644 (file)
@@ -84,11 +84,5 @@ error: unnecessary structure name repetition
 LL |     fn mul(self, rhs: Bad) -> Bad {
    |                               ^^^ help: use the applicable keyword: `Self`
 
-error: unnecessary structure name repetition
-  --> $DIR/use_self_trait.rs:50:9
-   |
-LL |         Bad
-   |         ^^^ help: use the applicable keyword: `Self`
-
-error: aborting due to 15 previous errors
+error: aborting due to 14 previous errors
 
index 642ce5040096eeab6356700737ebc203d30fe35d..5099aad83bcbc78c1e32a8be3efddf760b203c9e 100644 (file)
@@ -12,10 +12,35 @@ fn main() {
     cap_err.push(0);
     cap_err.push(1);
     cap_err.push(2);
+    if true {
+        // don't include this one
+        cap_err.push(3);
+    }
 
     let mut cap_ok = Vec::with_capacity(10);
     cap_ok.push(0);
 
     new_err = Vec::new();
     new_err.push(0);
+
+    let mut vec = Vec::new();
+    // control flow at block final expression
+    if true {
+        // no lint
+        vec.push(1);
+    }
+}
+
+pub fn no_lint() -> Vec<i32> {
+    let mut p = Some(1);
+    let mut vec = Vec::new();
+    loop {
+        match p {
+            None => return vec,
+            Some(i) => {
+                vec.push(i);
+                p = None;
+            },
+        }
+    }
 }
index 819ed47d0991989966686299a5455308042fb587..9ec3e10e62470559cb304b27c6ea89f7f6700bf9 100644 (file)
@@ -24,7 +24,7 @@ LL | |     cap_err.push(2);
    | |____________________^ help: consider using the `vec![]` macro: `let mut cap_err = vec![..];`
 
 error: calls to `push` immediately after creation
-  --> $DIR/vec_init_then_push.rs:19:5
+  --> $DIR/vec_init_then_push.rs:23:5
    |
 LL | /     new_err = Vec::new();
 LL | |     new_err.push(0);
index bc5ed0816cc812b139a5f7f299fbfdbb174b45fb..922a8207cea866c80cb482e0f631f8a52b096357 100644 (file)
@@ -2,21 +2,30 @@
 use rustc_tools_util::VersionInfo;
 
 #[test]
-fn check_that_clippy_lints_has_the_same_version_as_clippy() {
+fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() {
+    // do not run this test inside the upstream rustc repo:
+    // https://github.com/rust-lang/rust-clippy/issues/6683
+    if option_env!("RUSTC_TEST_SUITE").is_some() {
+        return;
+    }
+
     let clippy_meta = cargo_metadata::MetadataCommand::new()
         .no_deps()
         .exec()
         .expect("could not obtain cargo metadata");
-    std::env::set_current_dir(std::env::current_dir().unwrap().join("clippy_lints")).unwrap();
-    let clippy_lints_meta = cargo_metadata::MetadataCommand::new()
-        .no_deps()
-        .exec()
-        .expect("could not obtain cargo metadata");
-    assert_eq!(clippy_lints_meta.packages[0].version, clippy_meta.packages[0].version);
-    for package in &clippy_meta.packages[0].dependencies {
-        if package.name == "clippy_lints" {
-            assert!(package.req.matches(&clippy_lints_meta.packages[0].version));
-            return;
+
+    for krate in &["clippy_lints", "clippy_utils"] {
+        let krate_meta = clippy_meta
+            .packages
+            .iter()
+            .find(|package| package.name == *krate)
+            .expect("could not obtain cargo metadata");
+        assert_eq!(krate_meta.version, clippy_meta.packages[0].version);
+        for package in &clippy_meta.packages[0].dependencies {
+            if package.name == *krate {
+                assert!(package.req.matches(&krate_meta.version));
+                break;
+            }
         }
     }
 }
index 5f263ea87db1804796faf0fb9986f4a372d0fade..b32a6f08638cc028ee9ddaead485e0fb0ba240e1 100644 (file)
@@ -195,11 +195,17 @@ fn make_absolute(path: PathBuf) -> PathBuf {
 
     let src_base = opt_path(matches, "src-base");
     let run_ignored = matches.opt_present("ignored");
-    let has_tidy = Command::new("tidy")
-        .arg("--version")
-        .stdout(Stdio::null())
-        .status()
-        .map_or(false, |status| status.success());
+    let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode");
+    let has_tidy = if mode == Mode::Rustdoc {
+        Command::new("tidy")
+            .arg("--version")
+            .stdout(Stdio::null())
+            .status()
+            .map_or(false, |status| status.success())
+    } else {
+        // Avoid spawning an external command when we know tidy won't be used.
+        false
+    };
     Config {
         bless: matches.opt_present("bless"),
         compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
@@ -218,7 +224,7 @@ fn make_absolute(path: PathBuf) -> PathBuf {
         src_base,
         build_base: opt_path(matches, "build-base"),
         stage_id: matches.opt_str("stage-id").unwrap(),
-        mode: matches.opt_str("mode").unwrap().parse().expect("invalid mode"),
+        mode,
         suite: matches.opt_str("suite").unwrap(),
         debugger: None,
         run_ignored,
index 9f68c55ec975c1f4ded226b469c074993cd20c76..75c43343023f9a08ece28e7e1dfdbf092c9d8c73 100644 (file)
@@ -289,7 +289,7 @@ fn skip(path: &Path) -> bool {
                     suppressible_tidy_err!(err, skip_undocumented_unsafe, "undocumented unsafe");
                 }
             }
-            if line.contains("// SAFETY:") || line.contains("// Safety:") {
+            if line.contains("// SAFETY:") {
                 last_safety_comment = true;
             } else if line.trim().starts_with("//") || line.trim().is_empty() {
                 // keep previous value