about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md35
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md35
-rw-r--r--src/tools/clippy/.github/workflows/clippy.yml3
-rw-r--r--src/tools/clippy/CHANGELOG.md1
-rw-r--r--src/tools/clippy/Cargo.toml4
-rw-r--r--src/tools/clippy/README.md43
-rw-r--r--src/tools/clippy/clippy_dev/src/ra_setup.rs4
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_lints/src/default.rs188
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs83
-rw-r--r--src/tools/clippy/clippy_lints/src/large_enum_variant.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/macro_use.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_async_fn.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/map_err_ignore.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs60
-rw-r--r--src/tools/clippy/clippy_lints/src/single_component_path_imports.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_unit.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/ast_utils.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/ptr.rs1
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/macro_rules.rs10
-rw-r--r--src/tools/clippy/tests/ui/field_reassign_with_default.rs12
-rw-r--r--src/tools/clippy/tests/ui/field_reassign_with_default.stderr4
-rw-r--r--src/tools/clippy/tests/ui/from_over_into.rs21
-rw-r--r--src/tools/clippy/tests/ui/from_over_into.stderr15
-rw-r--r--src/tools/clippy/tests/ui/large_enum_variant.rs9
-rw-r--r--src/tools/clippy/tests/ui/large_enum_variant.stderr18
-rw-r--r--src/tools/clippy/tests/ui/map_err.stderr2
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_attr.rs8
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_attr.stderr8
-rw-r--r--src/tools/clippy/tests/ui/ptr_arg.rs41
-rw-r--r--src/tools/clippy/tests/ui/ptr_arg.stderr102
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.fixed1
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.rs1
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.stderr38
-rw-r--r--src/tools/clippy/tests/versioncheck.rs53
-rw-r--r--src/tools/clippy/util/gh-pages/index.html54
39 files changed, 647 insertions, 249 deletions
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md
new file mode 100644
index 00000000000..f46828fec91
--- /dev/null
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md
@@ -0,0 +1,35 @@
+---
+name: Bug Report (False Negative)
+about: Create a bug report about missing warnings from a lint
+labels: L-bug, L-false-negative
+---
+<!--
+Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
+along with any information you feel relevant to replicating the bug.
+-->
+Lint name:
+
+
+I tried this code:
+
+```rust
+<code>
+```
+
+I expected to see this happen: *explanation*
+
+Instead, this happened: *explanation*
+
+### Meta
+
+- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
+- `rustc -Vv`:
+  ```
+  rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+  binary: rustc
+  commit-hash: f455e46eae1a227d735091091144601b467e1565
+  commit-date: 2020-06-20
+  host: x86_64-unknown-linux-gnu
+  release: 1.46.0-nightly
+  LLVM version: 10.0
+  ```
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md
new file mode 100644
index 00000000000..92a7373fc27
--- /dev/null
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md
@@ -0,0 +1,35 @@
+---
+name: Bug Report (False Positive)
+about: Create a bug report about a wrongly emitted lint warning
+labels: L-bug, L-false-positive
+---
+<!--
+Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
+along with any information you feel relevant to replicating the bug.
+-->
+Lint name:
+
+
+I tried this code:
+
+```rust
+<code>
+```
+
+I expected to see this happen: *explanation*
+
+Instead, this happened: *explanation*
+
+### Meta
+
+- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
+- `rustc -Vv`:
+  ```
+  rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+  binary: rustc
+  commit-hash: f455e46eae1a227d735091091144601b467e1565
+  commit-date: 2020-06-20
+  host: x86_64-unknown-linux-gnu
+  release: 1.46.0-nightly
+  LLVM version: 10.0
+  ```
diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml
index 530e60001f7..9d5e12aac5f 100644
--- a/src/tools/clippy/.github/workflows/clippy.yml
+++ b/src/tools/clippy/.github/workflows/clippy.yml
@@ -50,6 +50,9 @@ jobs:
     - name: Build
       run: cargo build --features deny-warnings,internal-lints
 
+    - 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
 
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index af3b1c1db2a..de8da99cdee 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -1841,6 +1841,7 @@ Released 2018-09-13
 [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
 [`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
 [`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
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index a765390c603..e60aa472846 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.0.212"
+version = "0.1.51"
 authors = [
 	"Manish Goregaokar <manishsmail@gmail.com>",
 	"Andre Bogus <bogusandre@gmail.com>",
@@ -29,7 +29,7 @@ path = "src/driver.rs"
 
 [dependencies]
 # begin automatic update
-clippy_lints = { version = "0.0.212", path = "clippy_lints" }
+clippy_lints = { version = "0.1.50", path = "clippy_lints" }
 # end automatic update
 semver = "0.11"
 rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" }
diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md
index aaa55e11c7d..a4928e17e6a 100644
--- a/src/tools/clippy/README.md
+++ b/src/tools/clippy/README.md
@@ -10,16 +10,16 @@ A collection of lints to catch common mistakes and improve your [Rust](https://g
 Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
 You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
 
-Category | Description | Default level
--- | -- | --
-`clippy::all` | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny**
-`clippy::correctness` | code that is outright wrong or very useless | **deny**
-`clippy::style` | code that should be written in a more idiomatic way | **warn**
-`clippy::complexity` | code that does something simple but in a complex way | **warn**
-`clippy::perf` | code that can be written to run faster | **warn**
-`clippy::pedantic` | lints which are rather strict or might have false positives | allow
-`clippy::nursery` | new lints that are still under development | allow
-`clippy::cargo` | lints for the cargo manifest | allow
+| Category              | Description                                                             | Default level |
+| --------------------- | ----------------------------------------------------------------------- | ------------- |
+| `clippy::all`         | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny** |
+| `clippy::correctness` | code that is outright wrong or very useless                             | **deny**      |
+| `clippy::style`       | code that should be written in a more idiomatic way                     | **warn**      |
+| `clippy::complexity`  | code that does something simple but in a complex way                    | **warn**      |
+| `clippy::perf`        | code that can be written to run faster                                  | **warn**      |
+| `clippy::pedantic`    | lints which are rather strict or might have false positives             | allow         |
+| `clippy::nursery`     | new lints that are still under development                              | allow         |
+| `clippy::cargo`       | lints for the cargo manifest                                            | allow         |
 
 More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
 
@@ -98,17 +98,6 @@ If you want to run Clippy **only** on the given crate, use the `--no-deps` optio
 cargo clippy -p example -- --no-deps 
 ```
 
-### Running Clippy from the command line without installing it
-
-To have cargo compile your crate with Clippy without Clippy installation
-in your code, you can use:
-
-```terminal
-cargo run --bin cargo-clippy --manifest-path=path_to_clippys_Cargo.toml
-```
-
-*Note:* Be sure that Clippy was compiled with the same version of rustc that cargo invokes here!
-
 ### Travis CI
 
 You can add Clippy to Travis CI in the same way you use it locally:
@@ -130,18 +119,6 @@ script:
   # etc.
 ```
 
-If you are on nightly, It might happen that Clippy is not available for a certain nightly release.
-In this case you can try to conditionally install Clippy from the Git repo.
-
-```yaml
-language: rust
-rust:
-  - nightly
-before_script:
-   - rustup component add clippy --toolchain=nightly || cargo install --git https://github.com/rust-lang/rust-clippy/ --force clippy
-   # etc.
-```
-
 Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code.
 That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause
 an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command
diff --git a/src/tools/clippy/clippy_dev/src/ra_setup.rs b/src/tools/clippy/clippy_dev/src/ra_setup.rs
index 40bf4a9505a..5f5048e79e7 100644
--- a/src/tools/clippy/clippy_dev/src/ra_setup.rs
+++ b/src/tools/clippy/clippy_dev/src/ra_setup.rs
@@ -3,7 +3,7 @@
 use std::fs;
 use std::fs::File;
 use std::io::prelude::*;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 
 // This module takes an absolute path to a rustc repo and alters the dependencies to point towards
 // the respective rustc subcrates instead of using extern crate xyz.
@@ -44,7 +44,7 @@ pub fn run(rustc_path: Option<&str>) {
 }
 
 fn inject_deps_into_manifest(
-    rustc_source_dir: &PathBuf,
+    rustc_source_dir: &Path,
     manifest_path: &str,
     cargo_toml: &str,
     lib_rs: &str,
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index 7697eba650a..a9516560a61 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "clippy_lints"
 # begin automatic update
-version = "0.0.212"
+version = "0.1.51"
 # end automatic update
 authors = [
 	"Manish Goregaokar <manishsmail@gmail.com>",
diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs
index f69f6f1412a..b0d7c7b3baa 100644
--- a/src/tools/clippy/clippy_lints/src/default.rs
+++ b/src/tools/clippy/clippy_lints/src/default.rs
@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, Adt, Ty};
+use rustc_middle::ty;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::{Ident, Symbol};
 use rustc_span::Span;
@@ -103,18 +103,41 @@ impl LateLintPass<'_> for Default {
     }
 
     fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
-        // find all binding statements like `let mut _ = T::default()` where `T::default()` is the
-        // `default` method of the `Default` trait, and store statement index in current block being
-        // checked and the name of the bound variable
-        let binding_statements_using_default = enumerate_bindings_using_default(cx, block);
-
         // start from the `let mut _ = _::default();` and look at all the following
         // statements, see if they re-assign the fields of the binding
-        for (stmt_idx, binding_name, binding_type, span) in binding_statements_using_default {
-            // the last statement of a block cannot trigger the lint
-            if stmt_idx == block.stmts.len() - 1 {
-                break;
-            }
+        let stmts_head = match block.stmts {
+            // Skip the last statement since there cannot possibly be any following statements that re-assign fields.
+            [head @ .., _] if !head.is_empty() => head,
+            _ => return,
+        };
+        for (stmt_idx, stmt) in stmts_head.iter().enumerate() {
+            // find all binding statements like `let mut _ = T::default()` where `T::default()` is the
+            // `default` method of the `Default` trait, and store statement index in current block being
+            // checked and the name of the bound variable
+            let (local, variant, binding_name, binding_type, span) = if_chain! {
+                // only take `let ...` statements
+                if let StmtKind::Local(local) = stmt.kind;
+                if let Some(expr) = local.init;
+                // only take bindings to identifiers
+                if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
+                // only when assigning `... = Default::default()`
+                if is_expr_default(expr, cx);
+                let binding_type = cx.typeck_results().node_type(binding_id);
+                if let Some(adt) = binding_type.ty_adt_def();
+                if adt.is_struct();
+                let variant = adt.non_enum_variant();
+                if adt.did.is_local() || !variant.is_field_list_non_exhaustive();
+                let module_did = cx.tcx.parent_module(stmt.hir_id).to_def_id();
+                if variant
+                    .fields
+                    .iter()
+                    .all(|field| field.vis.is_accessible_from(module_did, cx.tcx));
+                then {
+                    (local, variant, ident.name, binding_type, expr.span)
+                } else {
+                    continue;
+                }
+            };
 
             // find all "later statement"'s where the fields of the binding set as
             // Default::default() get reassigned, unless the reassignment refers to the original binding
@@ -122,15 +145,8 @@ impl LateLintPass<'_> for Default {
             let mut assigned_fields = Vec::new();
             let mut cancel_lint = false;
             for consecutive_statement in &block.stmts[stmt_idx + 1..] {
-                // interrupt if the statement is a let binding (`Local`) that shadows the original
-                // binding
-                if stmt_shadows_binding(consecutive_statement, binding_name) {
-                    break;
-                }
                 // find out if and which field was set by this `consecutive_statement`
-                else if let Some((field_ident, assign_rhs)) =
-                    field_reassigned_by_stmt(consecutive_statement, binding_name)
-                {
+                if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) {
                     // interrupt and cancel lint if assign_rhs references the original binding
                     if contains_name(binding_name, assign_rhs) {
                         cancel_lint = true;
@@ -152,7 +168,7 @@ impl LateLintPass<'_> for Default {
                         first_assign = Some(consecutive_statement);
                     }
                 }
-                // interrupt also if no field was assigned, since we only want to look at consecutive statements
+                // interrupt if no field was assigned, since we only want to look at consecutive statements
                 else {
                     break;
                 }
@@ -161,55 +177,45 @@ impl LateLintPass<'_> for Default {
             // if there are incorrectly assigned fields, do a span_lint_and_note to suggest
             // construction using `Ty { fields, ..Default::default() }`
             if !assigned_fields.is_empty() && !cancel_lint {
-                // take the original assignment as span
-                let stmt = &block.stmts[stmt_idx];
-
-                if let StmtKind::Local(preceding_local) = &stmt.kind {
-                    // filter out fields like `= Default::default()`, because the FRU already covers them
-                    let assigned_fields = assigned_fields
-                        .into_iter()
-                        .filter(|(_, rhs)| !is_expr_default(rhs, cx))
-                        .collect::<Vec<(Symbol, &Expr<'_>)>>();
+                // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
+                let ext_with_default = !variant
+                    .fields
+                    .iter()
+                    .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.ident.name));
 
-                    // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
-                    let ext_with_default = !fields_of_type(binding_type)
-                        .iter()
-                        .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name));
+                let field_list = assigned_fields
+                    .into_iter()
+                    .map(|(field, rhs)| {
+                        // extract and store the assigned value for help message
+                        let value_snippet = snippet(cx, rhs.span, "..");
+                        format!("{}: {}", field, value_snippet)
+                    })
+                    .collect::<Vec<String>>()
+                    .join(", ");
 
-                    let field_list = assigned_fields
-                        .into_iter()
-                        .map(|(field, rhs)| {
-                            // extract and store the assigned value for help message
-                            let value_snippet = snippet(cx, rhs.span, "..");
-                            format!("{}: {}", field, value_snippet)
-                        })
-                        .collect::<Vec<String>>()
-                        .join(", ");
-
-                    let sugg = if ext_with_default {
-                        if field_list.is_empty() {
-                            format!("{}::default()", binding_type)
-                        } else {
-                            format!("{} {{ {}, ..Default::default() }}", binding_type, field_list)
-                        }
+                let sugg = if ext_with_default {
+                    if field_list.is_empty() {
+                        format!("{}::default()", binding_type)
                     } else {
-                        format!("{} {{ {} }}", binding_type, field_list)
-                    };
+                        format!("{} {{ {}, ..Default::default() }}", binding_type, field_list)
+                    }
+                } else {
+                    format!("{} {{ {} }}", binding_type, field_list)
+                };
 
-                    // span lint once per statement that binds default
-                    span_lint_and_note(
-                        cx,
-                        FIELD_REASSIGN_WITH_DEFAULT,
-                        first_assign.unwrap().span,
-                        "field assignment outside of initializer for an instance created with Default::default()",
-                        Some(preceding_local.span),
-                        &format!(
-                            "consider initializing the variable with `{}` and removing relevant reassignments",
-                            sugg
-                        ),
-                    );
-                    self.reassigned_linted.insert(span);
-                }
+                // span lint once per statement that binds default
+                span_lint_and_note(
+                    cx,
+                    FIELD_REASSIGN_WITH_DEFAULT,
+                    first_assign.unwrap().span,
+                    "field assignment outside of initializer for an instance created with Default::default()",
+                    Some(local.span),
+                    &format!(
+                        "consider initializing the variable with `{}` and removing relevant reassignments",
+                        sugg
+                    ),
+                );
+                self.reassigned_linted.insert(span);
             }
         }
     }
@@ -230,47 +236,6 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool
     }
 }
 
-/// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except
-/// for when the pattern type is a tuple.
-fn enumerate_bindings_using_default<'tcx>(
-    cx: &LateContext<'tcx>,
-    block: &Block<'tcx>,
-) -> Vec<(usize, Symbol, Ty<'tcx>, Span)> {
-    block
-        .stmts
-        .iter()
-        .enumerate()
-        .filter_map(|(idx, stmt)| {
-            if_chain! {
-                // only take `let ...` statements
-                if let StmtKind::Local(ref local) = stmt.kind;
-                // only take bindings to identifiers
-                if let PatKind::Binding(_, _, ident, _) = local.pat.kind;
-                // that are not tuples
-                let ty = cx.typeck_results().pat_ty(local.pat);
-                if !matches!(ty.kind(), ty::Tuple(_));
-                // only when assigning `... = Default::default()`
-                if let Some(ref expr) = local.init;
-                if is_expr_default(expr, cx);
-                then {
-                    Some((idx, ident.name, ty, expr.span))
-                } else {
-                    None
-                }
-            }
-        })
-        .collect()
-}
-
-fn stmt_shadows_binding(this: &Stmt<'_>, shadowed: Symbol) -> bool {
-    if let StmtKind::Local(local) = &this.kind {
-        if let PatKind::Binding(_, _, ident, _) = local.pat.kind {
-            return ident.name == shadowed;
-        }
-    }
-    false
-}
-
 /// Returns the reassigned field and the assigning expression (right-hand side of assign).
 fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> {
     if_chain! {
@@ -290,14 +255,3 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op
         }
     }
 }
-
-/// Returns the vec of fields for a struct and an empty vec for non-struct ADTs.
-fn fields_of_type(ty: Ty<'_>) -> Vec<Ident> {
-    if let Adt(adt, _) = ty.kind() {
-        if adt.is_struct() {
-            let variant = &adt.non_enum_variant();
-            return variant.fields.iter().map(|f| f.ident).collect();
-        }
-    }
-    vec![]
-}
diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs
new file mode 100644
index 00000000000..1e7e5f53cc2
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -0,0 +1,83 @@
+use crate::utils::paths::INTO;
+use crate::utils::{match_def_path, meets_msrv, span_lint_and_help};
+use if_chain::if_chain;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+const FROM_OVER_INTO_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
+
+declare_clippy_lint! {
+    /// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.
+    ///
+    /// **Why is this bad?** According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// struct StringWrapper(String);
+    ///
+    /// impl Into<StringWrapper> for String {
+    ///     fn into(self) -> StringWrapper {
+    ///         StringWrapper(self)
+    ///     }
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// struct StringWrapper(String);
+    ///
+    /// impl From<String> for StringWrapper {
+    ///     fn from(s: String) -> StringWrapper {
+    ///         StringWrapper(s)
+    ///     }
+    /// }
+    /// ```
+    pub FROM_OVER_INTO,
+    style,
+    "Warns on implementations of `Into<..>` to use `From<..>`"
+}
+
+pub struct FromOverInto {
+    msrv: Option<RustcVersion>,
+}
+
+impl FromOverInto {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        FromOverInto { msrv }
+    }
+}
+
+impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
+
+impl LateLintPass<'_> for FromOverInto {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+        if !meets_msrv(self.msrv.as_ref(), &FROM_OVER_INTO_MSRV) {
+            return;
+        }
+
+        let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id);
+        if_chain! {
+            if let hir::ItemKind::Impl{ .. } = &item.kind;
+            if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id);
+            if match_def_path(cx, impl_trait_ref.def_id, &INTO);
+
+            then {
+                span_lint_and_help(
+                    cx,
+                    FROM_OVER_INTO,
+                    item.span,
+                    "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true",
+                    None,
+                    "consider to implement `From` instead",
+                );
+            }
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
diff --git a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
index 3c7880d74ee..ad9b4f357a7 100644
--- a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
+++ b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
@@ -4,6 +4,7 @@ use crate::utils::{snippet_opt, span_lint_and_then};
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind, VariantData};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_target::abi::LayoutOf;
 
@@ -58,6 +59,9 @@ impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
 
 impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+        if in_external_macro(cx.tcx.sess, item.span) {
+            return;
+        }
         let did = cx.tcx.hir().local_def_id(item.hir_id);
         if let ItemKind::Enum(ref def, _) = item.kind {
             let ty = cx.tcx.type_of(did);
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 02ba422a2f5..35b057d7b6a 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -207,6 +207,7 @@ mod float_literal;
 mod floating_point_arithmetic;
 mod format;
 mod formatting;
+mod from_over_into;
 mod functions;
 mod future_not_send;
 mod get_last_with_len;
@@ -614,6 +615,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
         &formatting::SUSPICIOUS_ELSE_FORMATTING,
         &formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
+        &from_over_into::FROM_OVER_INTO,
         &functions::DOUBLE_MUST_USE,
         &functions::MUST_USE_CANDIDATE,
         &functions::MUST_USE_UNIT,
@@ -1014,6 +1016,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv));
     store.register_late_pass(move || box mem_replace::MemReplace::new(msrv));
     store.register_late_pass(move || box ranges::Ranges::new(msrv));
+    store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv));
     store.register_late_pass(move || box use_self::UseSelf::new(msrv));
     store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv));
 
@@ -1417,6 +1420,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
         LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
         LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
+        LintId::of(&from_over_into::FROM_OVER_INTO),
         LintId::of(&functions::DOUBLE_MUST_USE),
         LintId::of(&functions::MUST_USE_UNIT),
         LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
@@ -1663,6 +1667,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
         LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
         LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
+        LintId::of(&from_over_into::FROM_OVER_INTO),
         LintId::of(&functions::DOUBLE_MUST_USE),
         LintId::of(&functions::MUST_USE_UNIT),
         LintId::of(&functions::RESULT_UNIT_ERR),
diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs
index b4b4b3dc18d..bb52888883a 100644
--- a/src/tools/clippy/clippy_lints/src/macro_use.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_use.rs
@@ -105,7 +105,7 @@ impl MacroUseImports {
 impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
         if_chain! {
-            if cx.sess().opts.edition == Edition::Edition2018;
+            if cx.sess().opts.edition >= Edition::Edition2018;
             if let hir::ItemKind::Use(path, _kind) = &item.kind;
             if let Some(mac_attr) = item
                 .attrs
diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
index 7b3b450ef93..29439e52c48 100644
--- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
@@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
                     |diag| {
                         if_chain! {
                             if let Some(header_snip) = snippet_opt(cx, header_span);
-                            if let Some(ret_pos) = position_before_rarrow(header_snip.clone());
+                            if let Some(ret_pos) = position_before_rarrow(&header_snip);
                             if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output);
                             then {
                                 let help = format!("make the function `async` and {}", ret_sugg);
diff --git a/src/tools/clippy/clippy_lints/src/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/map_err_ignore.rs
index f3c0515b9bc..76fe8e776ea 100644
--- a/src/tools/clippy/clippy_lints/src/map_err_ignore.rs
+++ b/src/tools/clippy/clippy_lints/src/map_err_ignore.rs
@@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
 declare_clippy_lint! {
     /// **What it does:** Checks for instances of `map_err(|_| Some::Enum)`
     ///
-    /// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to contain and report the cause of the error
+    /// **Why is this bad?** This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
     ///
     /// **Known problems:** None.
     ///
@@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
                                     body_span,
                                     "`map_err(|_|...` wildcard pattern discards the original error",
                                     None,
-                                    "Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
+                                    "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
                                 );
                             }
                         }
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index dcb643a28ae..c494a713631 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -182,20 +182,6 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
 
         if let ty::Ref(_, ty, Mutability::Not) = ty.kind() {
             if is_type_diagnostic_item(cx, ty, sym::vec_type) {
-                let mut ty_snippet = None;
-                if_chain! {
-                    if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind;
-                    if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last();
-                    then {
-                        let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg {
-                            GenericArg::Type(ty) => Some(ty),
-                            _ => None,
-                        }).collect();
-                        if types.len() == 1 {
-                            ty_snippet = snippet_opt(cx, types[0].span);
-                        }
-                    }
-                };
                 if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
                     span_lint_and_then(
                         cx,
@@ -204,7 +190,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
                         "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
                          with non-Vec-based slices.",
                         |diag| {
-                            if let Some(ref snippet) = ty_snippet {
+                            if let Some(ref snippet) = get_only_generic_arg_snippet(cx, arg) {
                                 diag.span_suggestion(
                                     arg.span,
                                     "change this to",
@@ -247,6 +233,33 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
                         },
                     );
                 }
+            } else if match_type(cx, ty, &paths::PATH_BUF) {
+                if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_path_buf()"), ("as_path", "")]) {
+                    span_lint_and_then(
+                        cx,
+                        PTR_ARG,
+                        arg.span,
+                        "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.",
+                        |diag| {
+                            diag.span_suggestion(
+                                arg.span,
+                                "change this to",
+                                "&Path".into(),
+                                Applicability::Unspecified,
+                            );
+                            for (clonespan, suggestion) in spans {
+                                diag.span_suggestion_short(
+                                    clonespan,
+                                    &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
+                                        Cow::Owned(format!("change `{}` to", x))
+                                    }),
+                                    suggestion.into(),
+                                    Applicability::Unspecified,
+                                );
+                            }
+                        },
+                    );
+                }
             } else if match_type(cx, ty, &paths::COW) {
                 if_chain! {
                     if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind;
@@ -309,6 +322,23 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
     }
 }
 
+fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option<String> {
+    if_chain! {
+        if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind;
+        if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last();
+        let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg {
+            GenericArg::Type(ty) => Some(ty),
+            _ => None,
+        }).collect();
+        if types.len() == 1;
+        then {
+            snippet_opt(cx, types[0].span)
+        } else {
+            None
+        }
+    }
+}
+
 fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
     if let TyKind::Rptr(ref lt, ref m) = ty.kind {
         Some((lt, m.mutbl, ty.span))
diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
index 35b38eca14d..1fc4ff5c2e6 100644
--- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
@@ -40,7 +40,7 @@ impl EarlyLintPass for SingleComponentPathImports {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
         if_chain! {
             if !in_macro(item.span);
-            if cx.sess.opts.edition == Edition::Edition2018;
+            if cx.sess.opts.edition >= Edition::Edition2018;
             if !item.vis.kind.is_pub();
             if let ItemKind::Use(use_tree) = &item.kind;
             if let segments = &use_tree.prefix.segments;
diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs
index f61fd2ecd73..a31cd5fda84 100644
--- a/src/tools/clippy/clippy_lints/src/unused_unit.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs
@@ -120,7 +120,7 @@ fn is_unit_expr(expr: &ast::Expr) -> bool {
 
 fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
     let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) {
-        position_before_rarrow(fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
+        position_before_rarrow(&fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
             (
                 #[allow(clippy::cast_possible_truncation)]
                 ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
diff --git a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
index 940573e4caa..5aed676fceb 100644
--- a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
@@ -501,8 +501,18 @@ pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
         && 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)),
+            (
+                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))
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index 424856090f2..1c68e837c4a 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -788,8 +788,7 @@ pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
 /// fn into3(self)   -> () {}
 ///               ^
 /// ```
-#[allow(clippy::needless_pass_by_value)]
-pub fn position_before_rarrow(s: String) -> Option<usize> {
+pub fn position_before_rarrow(s: &str) -> Option<usize> {
     s.rfind("->").map(|rpos| {
         let mut rpos = rpos;
         let chars: Vec<char> = s.chars().collect();
diff --git a/src/tools/clippy/clippy_lints/src/utils/ptr.rs b/src/tools/clippy/clippy_lints/src/utils/ptr.rs
index bd2c619f000..b330f3d890e 100644
--- a/src/tools/clippy/clippy_lints/src/utils/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/ptr.rs
@@ -72,7 +72,6 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
                     }
                 }
             }
-            return;
         }
         walk_expr(self, expr);
     }
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index d2e84132f4e..c579beeae89 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2020-12-20"
+channel = "nightly-2021-01-02"
 components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"]
diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
index f985a15eda2..18324823468 100644
--- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
@@ -84,3 +84,13 @@ macro_rules! as_conv {
         0u32 as u64
     };
 }
+
+#[macro_export]
+macro_rules! large_enum_variant {
+    () => {
+        enum LargeEnumInMacro {
+            A(i32),
+            B([i32; 8000]),
+        }
+    };
+}
diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.rs b/src/tools/clippy/tests/ui/field_reassign_with_default.rs
index 79a30c22f95..3e0921022b4 100644
--- a/src/tools/clippy/tests/ui/field_reassign_with_default.rs
+++ b/src/tools/clippy/tests/ui/field_reassign_with_default.rs
@@ -107,4 +107,16 @@ fn main() {
     x.i = side_effect.next();
     x.j = 2;
     x.i = side_effect.next();
+
+    // don't lint - some private fields
+    let mut x = m::F::default();
+    x.a = 1;
+}
+
+mod m {
+    #[derive(Default)]
+    pub struct F {
+        pub a: u64,
+        b: u64,
+    }
 }
diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr
index c788ebae552..9a2bc778c3f 100644
--- a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr
+++ b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr
@@ -53,7 +53,7 @@ error: field assignment outside of initializer for an instance created with Defa
 LL |     a.i = Default::default();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: consider initializing the variable with `A::default()` and removing relevant reassignments
+note: consider initializing the variable with `A { i: Default::default(), ..Default::default() }` and removing relevant reassignments
   --> $DIR/field_reassign_with_default.rs:90:5
    |
 LL |     let mut a: A = Default::default();
@@ -65,7 +65,7 @@ error: field assignment outside of initializer for an instance created with Defa
 LL |     a.i = Default::default();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: consider initializing the variable with `A { j: 45, ..Default::default() }` and removing relevant reassignments
+note: consider initializing the variable with `A { i: Default::default(), j: 45 }` and removing relevant reassignments
   --> $DIR/field_reassign_with_default.rs:94:5
    |
 LL |     let mut a: A = Default::default();
diff --git a/src/tools/clippy/tests/ui/from_over_into.rs b/src/tools/clippy/tests/ui/from_over_into.rs
new file mode 100644
index 00000000000..292d0924fb1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/from_over_into.rs
@@ -0,0 +1,21 @@
+#![warn(clippy::from_over_into)]
+
+// this should throw an error
+struct StringWrapper(String);
+
+impl Into<StringWrapper> for String {
+    fn into(self) -> StringWrapper {
+        StringWrapper(self)
+    }
+}
+
+// this is fine
+struct A(String);
+
+impl From<String> for A {
+    fn from(s: String) -> A {
+        A(s)
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/from_over_into.stderr b/src/tools/clippy/tests/ui/from_over_into.stderr
new file mode 100644
index 00000000000..18f56f85432
--- /dev/null
+++ b/src/tools/clippy/tests/ui/from_over_into.stderr
@@ -0,0 +1,15 @@
+error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
+  --> $DIR/from_over_into.rs:6:1
+   |
+LL | / impl Into<StringWrapper> for String {
+LL | |     fn into(self) -> StringWrapper {
+LL | |         StringWrapper(self)
+LL | |     }
+LL | | }
+   | |_^
+   |
+   = note: `-D clippy::from-over-into` implied by `-D warnings`
+   = help: consider to implement `From` instead
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/large_enum_variant.rs b/src/tools/clippy/tests/ui/large_enum_variant.rs
index 852ef5fec0e..d22fee3f27b 100644
--- a/src/tools/clippy/tests/ui/large_enum_variant.rs
+++ b/src/tools/clippy/tests/ui/large_enum_variant.rs
@@ -1,7 +1,12 @@
+// aux-build:macro_rules.rs
+
 #![allow(dead_code)]
 #![allow(unused_variables)]
 #![warn(clippy::large_enum_variant)]
 
+#[macro_use]
+extern crate macro_rules;
+
 enum LargeEnum {
     A(i32),
     B([i32; 8000]),
@@ -51,4 +56,6 @@ enum LargeEnumOk {
     LargeB([i32; 8001]),
 }
 
-fn main() {}
+fn main() {
+    large_enum_variant!();
+}
diff --git a/src/tools/clippy/tests/ui/large_enum_variant.stderr b/src/tools/clippy/tests/ui/large_enum_variant.stderr
index 8ce641a81f2..d39a4d462aa 100644
--- a/src/tools/clippy/tests/ui/large_enum_variant.stderr
+++ b/src/tools/clippy/tests/ui/large_enum_variant.stderr
@@ -1,12 +1,12 @@
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:7:5
+  --> $DIR/large_enum_variant.rs:12:5
    |
 LL |     B([i32; 8000]),
    |     ^^^^^^^^^^^^^^ this variant is 32000 bytes
    |
    = note: `-D clippy::large-enum-variant` implied by `-D warnings`
 note: and the second-largest variant is 4 bytes:
-  --> $DIR/large_enum_variant.rs:6:5
+  --> $DIR/large_enum_variant.rs:11:5
    |
 LL |     A(i32),
    |     ^^^^^^
@@ -16,13 +16,13 @@ LL |     B(Box<[i32; 8000]>),
    |       ^^^^^^^^^^^^^^^^
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:31:5
+  --> $DIR/large_enum_variant.rs:36:5
    |
 LL |     ContainingLargeEnum(LargeEnum),
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
    |
 note: and the second-largest variant is 8 bytes:
-  --> $DIR/large_enum_variant.rs:30:5
+  --> $DIR/large_enum_variant.rs:35:5
    |
 LL |     VariantOk(i32, u32),
    |     ^^^^^^^^^^^^^^^^^^^
@@ -32,30 +32,30 @@ LL |     ContainingLargeEnum(Box<LargeEnum>),
    |                         ^^^^^^^^^^^^^^
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:41:5
+  --> $DIR/large_enum_variant.rs:46:5
    |
 LL |     StructLikeLarge { x: [i32; 8000], y: i32 },
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
    |
 note: and the second-largest variant is 8 bytes:
-  --> $DIR/large_enum_variant.rs:40:5
+  --> $DIR/large_enum_variant.rs:45:5
    |
 LL |     VariantOk(i32, u32),
    |     ^^^^^^^^^^^^^^^^^^^
 help: consider boxing the large fields to reduce the total size of the enum
-  --> $DIR/large_enum_variant.rs:41:5
+  --> $DIR/large_enum_variant.rs:46:5
    |
 LL |     StructLikeLarge { x: [i32; 8000], y: i32 },
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: large size difference between variants
-  --> $DIR/large_enum_variant.rs:46:5
+  --> $DIR/large_enum_variant.rs:51:5
    |
 LL |     StructLikeLarge2 { x: [i32; 8000] },
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32000 bytes
    |
 note: and the second-largest variant is 8 bytes:
-  --> $DIR/large_enum_variant.rs:45:5
+  --> $DIR/large_enum_variant.rs:50:5
    |
 LL |     VariantOk(i32, u32),
    |     ^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/map_err.stderr b/src/tools/clippy/tests/ui/map_err.stderr
index 8ee2941790d..37e87e64de2 100644
--- a/src/tools/clippy/tests/ui/map_err.stderr
+++ b/src/tools/clippy/tests/ui/map_err.stderr
@@ -5,7 +5,7 @@ LL |     println!("{:?}", x.map_err(|_| Errors::Ignored));
    |                                ^^^
    |
    = note: `-D clippy::map-err-ignore` implied by `-D warnings`
-   = help: Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)
+   = help: consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)
 
 error: aborting due to previous error
 
diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_attr.rs
index 3848bca3207..0f47f1cbc40 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_attr.rs
+++ b/src/tools/clippy/tests/ui/min_rust_version_attr.rs
@@ -57,6 +57,14 @@ pub fn checked_conversion() {
     let _ = value <= (u32::MAX as i64) && value >= 0;
 }
 
+pub struct FromOverInto(String);
+
+impl Into<FromOverInto> for String {
+    fn into(self) -> FromOverInto {
+        FromOverInto(self)
+    }
+}
+
 pub fn filter_map_next() {
     let a = ["1", "lol", "3", "NaN", "5"];
 
diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
index 34805263104..e3e3b335cbe 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
+++ b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
@@ -1,12 +1,12 @@
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:142:24
+  --> $DIR/min_rust_version_attr.rs:150:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::manual-strip` implied by `-D warnings`
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:141:9
+  --> $DIR/min_rust_version_attr.rs:149:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,13 +17,13 @@ LL |             assert_eq!(<stripped>.to_uppercase(), "WORLD!");
    |
 
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:154:24
+  --> $DIR/min_rust_version_attr.rs:162:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:153:9
+  --> $DIR/min_rust_version_attr.rs:161:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs
index 541225e6351..06370dfce65 100644
--- a/src/tools/clippy/tests/ui/ptr_arg.rs
+++ b/src/tools/clippy/tests/ui/ptr_arg.rs
@@ -2,6 +2,7 @@
 #![warn(clippy::ptr_arg)]
 
 use std::borrow::Cow;
+use std::path::PathBuf;
 
 fn do_vec(x: &Vec<i64>) {
     //Nothing here
@@ -21,6 +22,15 @@ fn do_str_mut(x: &mut String) {
     //Nothing here either
 }
 
+fn do_path(x: &PathBuf) {
+    //Nothing here either
+}
+
+fn do_path_mut(x: &mut PathBuf) {
+    // no error here
+    //Nothing here either
+}
+
 fn main() {}
 
 trait Foo {
@@ -55,6 +65,14 @@ fn str_cloned(x: &String) -> String {
     x.clone()
 }
 
+fn path_cloned(x: &PathBuf) -> PathBuf {
+    let a = x.clone();
+    let b = x.clone();
+    let c = b.clone();
+    let d = a.clone().clone().clone();
+    x.clone()
+}
+
 fn false_positive_capacity(x: &Vec<u8>, y: &String) {
     let a = x.capacity();
     let b = y.clone();
@@ -87,10 +105,12 @@ impl Foo2 for String {
 // Check that the allow attribute on parameters is honored
 mod issue_5644 {
     use std::borrow::Cow;
+    use std::path::PathBuf;
 
     fn allowed(
         #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
         #[allow(clippy::ptr_arg)] _s: &String,
+        #[allow(clippy::ptr_arg)] _p: &PathBuf,
         #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
     ) {
     }
@@ -100,6 +120,7 @@ mod issue_5644 {
         fn allowed(
             #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
             #[allow(clippy::ptr_arg)] _s: &String,
+            #[allow(clippy::ptr_arg)] _p: &PathBuf,
             #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
         ) {
         }
@@ -109,8 +130,28 @@ mod issue_5644 {
         fn allowed(
             #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
             #[allow(clippy::ptr_arg)] _s: &String,
+            #[allow(clippy::ptr_arg)] _p: &PathBuf,
             #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
         ) {
         }
     }
 }
+
+mod issue6509 {
+    use std::path::PathBuf;
+
+    fn foo_vec(vec: &Vec<u8>) {
+        let _ = vec.clone().pop();
+        let _ = vec.clone().clone();
+    }
+
+    fn foo_path(path: &PathBuf) {
+        let _ = path.clone().pop();
+        let _ = path.clone().clone();
+    }
+
+    fn foo_str(str: &PathBuf) {
+        let _ = str.clone().pop();
+        let _ = str.clone().clone();
+    }
+}
diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr
index 314f23497f9..708318bbe29 100644
--- a/src/tools/clippy/tests/ui/ptr_arg.stderr
+++ b/src/tools/clippy/tests/ui/ptr_arg.stderr
@@ -1,5 +1,5 @@
 error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
-  --> $DIR/ptr_arg.rs:6:14
+  --> $DIR/ptr_arg.rs:7:14
    |
 LL | fn do_vec(x: &Vec<i64>) {
    |              ^^^^^^^^^ help: change this to: `&[i64]`
@@ -7,19 +7,25 @@ LL | fn do_vec(x: &Vec<i64>) {
    = note: `-D clippy::ptr-arg` implied by `-D warnings`
 
 error: writing `&String` instead of `&str` involves a new object where a slice will do.
-  --> $DIR/ptr_arg.rs:15:14
+  --> $DIR/ptr_arg.rs:16:14
    |
 LL | fn do_str(x: &String) {
    |              ^^^^^^^ help: change this to: `&str`
 
+error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.
+  --> $DIR/ptr_arg.rs:25:15
+   |
+LL | fn do_path(x: &PathBuf) {
+   |               ^^^^^^^^ help: change this to: `&Path`
+
 error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
-  --> $DIR/ptr_arg.rs:28:18
+  --> $DIR/ptr_arg.rs:38:18
    |
 LL |     fn do_vec(x: &Vec<i64>);
    |                  ^^^^^^^^^ help: change this to: `&[i64]`
 
 error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
-  --> $DIR/ptr_arg.rs:41:14
+  --> $DIR/ptr_arg.rs:51:14
    |
 LL | fn cloned(x: &Vec<u8>) -> Vec<u8> {
    |              ^^^^^^^^
@@ -38,7 +44,7 @@ LL |     x.to_owned()
    |
 
 error: writing `&String` instead of `&str` involves a new object where a slice will do.
-  --> $DIR/ptr_arg.rs:50:18
+  --> $DIR/ptr_arg.rs:60:18
    |
 LL | fn str_cloned(x: &String) -> String {
    |                  ^^^^^^^
@@ -60,8 +66,31 @@ help: change `x.clone()` to
 LL |     x.to_string()
    |
 
+error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.
+  --> $DIR/ptr_arg.rs:68:19
+   |
+LL | fn path_cloned(x: &PathBuf) -> PathBuf {
+   |                   ^^^^^^^^
+   |
+help: change this to
+   |
+LL | fn path_cloned(x: &Path) -> PathBuf {
+   |                   ^^^^^
+help: change `x.clone()` to
+   |
+LL |     let a = x.to_path_buf();
+   |             ^^^^^^^^^^^^^^^
+help: change `x.clone()` to
+   |
+LL |     let b = x.to_path_buf();
+   |             ^^^^^^^^^^^^^^^
+help: change `x.clone()` to
+   |
+LL |     x.to_path_buf()
+   |
+
 error: writing `&String` instead of `&str` involves a new object where a slice will do.
-  --> $DIR/ptr_arg.rs:58:44
+  --> $DIR/ptr_arg.rs:76:44
    |
 LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) {
    |                                            ^^^^^^^
@@ -80,10 +109,67 @@ LL |     let c = y;
    |             ^
 
 error: using a reference to `Cow` is not recommended.
-  --> $DIR/ptr_arg.rs:72:25
+  --> $DIR/ptr_arg.rs:90:25
    |
 LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
    |                         ^^^^^^^^^^^ help: change this to: `&[i32]`
 
-error: aborting due to 7 previous errors
+error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
+  --> $DIR/ptr_arg.rs:143:21
+   |
+LL |     fn foo_vec(vec: &Vec<u8>) {
+   |                     ^^^^^^^^
+   |
+help: change this to
+   |
+LL |     fn foo_vec(vec: &[u8]) {
+   |                     ^^^^^
+help: change `vec.clone()` to
+   |
+LL |         let _ = vec.to_owned().pop();
+   |                 ^^^^^^^^^^^^^^
+help: change `vec.clone()` to
+   |
+LL |         let _ = vec.to_owned().clone();
+   |                 ^^^^^^^^^^^^^^
+
+error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.
+  --> $DIR/ptr_arg.rs:148:23
+   |
+LL |     fn foo_path(path: &PathBuf) {
+   |                       ^^^^^^^^
+   |
+help: change this to
+   |
+LL |     fn foo_path(path: &Path) {
+   |                       ^^^^^
+help: change `path.clone()` to
+   |
+LL |         let _ = path.to_path_buf().pop();
+   |                 ^^^^^^^^^^^^^^^^^^
+help: change `path.clone()` to
+   |
+LL |         let _ = path.to_path_buf().clone();
+   |                 ^^^^^^^^^^^^^^^^^^
+
+error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.
+  --> $DIR/ptr_arg.rs:153:21
+   |
+LL |     fn foo_str(str: &PathBuf) {
+   |                     ^^^^^^^^
+   |
+help: change this to
+   |
+LL |     fn foo_str(str: &Path) {
+   |                     ^^^^^
+help: change `str.clone()` to
+   |
+LL |         let _ = str.to_path_buf().pop();
+   |                 ^^^^^^^^^^^^^^^^^
+help: change `str.clone()` to
+   |
+LL |         let _ = str.to_path_buf().clone();
+   |                 ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unused_unit.fixed b/src/tools/clippy/tests/ui/unused_unit.fixed
index 7afc5361356..a192ebde3eb 100644
--- a/src/tools/clippy/tests/ui/unused_unit.fixed
+++ b/src/tools/clippy/tests/ui/unused_unit.fixed
@@ -11,6 +11,7 @@
 
 #![deny(clippy::unused_unit)]
 #![allow(dead_code)]
+#![allow(clippy::from_over_into)]
 
 struct Unitter;
 impl Unitter {
diff --git a/src/tools/clippy/tests/ui/unused_unit.rs b/src/tools/clippy/tests/ui/unused_unit.rs
index 96cef1ed5a5..96041a7dd85 100644
--- a/src/tools/clippy/tests/ui/unused_unit.rs
+++ b/src/tools/clippy/tests/ui/unused_unit.rs
@@ -11,6 +11,7 @@
 
 #![deny(clippy::unused_unit)]
 #![allow(dead_code)]
+#![allow(clippy::from_over_into)]
 
 struct Unitter;
 impl Unitter {
diff --git a/src/tools/clippy/tests/ui/unused_unit.stderr b/src/tools/clippy/tests/ui/unused_unit.stderr
index c45634c2b6d..02038b5fb6b 100644
--- a/src/tools/clippy/tests/ui/unused_unit.stderr
+++ b/src/tools/clippy/tests/ui/unused_unit.stderr
@@ -1,5 +1,5 @@
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:18:28
+  --> $DIR/unused_unit.rs:19:28
    |
 LL |     pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> ()
    |                            ^^^^^^ help: remove the `-> ()`
@@ -11,109 +11,109 @@ LL | #![deny(clippy::unused_unit)]
    |         ^^^^^^^^^^^^^^^^^^^
 
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:19:18
+  --> $DIR/unused_unit.rs:20:18
    |
 LL |     where G: Fn() -> () {
    |                  ^^^^^^ help: remove the `-> ()`
 
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:18:58
+  --> $DIR/unused_unit.rs:19:58
    |
 LL |     pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> ()
    |                                                          ^^^^^^ help: remove the `-> ()`
 
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:20:26
+  --> $DIR/unused_unit.rs:21:26
    |
 LL |         let _y: &dyn Fn() -> () = &f;
    |                          ^^^^^^ help: remove the `-> ()`
 
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:27:18
+  --> $DIR/unused_unit.rs:28:18
    |
 LL |     fn into(self) -> () {
    |                  ^^^^^^ help: remove the `-> ()`
 
 error: unneeded unit expression
-  --> $DIR/unused_unit.rs:28:9
+  --> $DIR/unused_unit.rs:29:9
    |
 LL |         ()
    |         ^^ help: remove the final `()`
 
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:33:29
+  --> $DIR/unused_unit.rs:34:29
    |
 LL |     fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H)
    |                             ^^^^^^ help: remove the `-> ()`
 
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:35:19
+  --> $DIR/unused_unit.rs:36:19
    |
 LL |         G: FnMut() -> (),
    |                   ^^^^^^ help: remove the `-> ()`
 
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:36:16
+  --> $DIR/unused_unit.rs:37:16
    |
 LL |         H: Fn() -> ();
    |                ^^^^^^ help: remove the `-> ()`
 
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:40:29
+  --> $DIR/unused_unit.rs:41:29
    |
 LL |     fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H)
    |                             ^^^^^^ help: remove the `-> ()`
 
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:42:19
+  --> $DIR/unused_unit.rs:43:19
    |
 LL |         G: FnMut() -> (),
    |                   ^^^^^^ help: remove the `-> ()`
 
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:43:16
+  --> $DIR/unused_unit.rs:44:16
    |
 LL |         H: Fn() -> () {}
    |                ^^^^^^ help: remove the `-> ()`
 
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:46:17
+  --> $DIR/unused_unit.rs:47:17
    |
 LL | fn return_unit() -> () { () }
    |                 ^^^^^^ help: remove the `-> ()`
 
 error: unneeded unit expression
-  --> $DIR/unused_unit.rs:46:26
+  --> $DIR/unused_unit.rs:47:26
    |
 LL | fn return_unit() -> () { () }
    |                          ^^ help: remove the final `()`
 
 error: unneeded `()`
-  --> $DIR/unused_unit.rs:56:14
+  --> $DIR/unused_unit.rs:57:14
    |
 LL |         break();
    |              ^^ help: remove the `()`
 
 error: unneeded `()`
-  --> $DIR/unused_unit.rs:58:11
+  --> $DIR/unused_unit.rs:59:11
    |
 LL |     return();
    |           ^^ help: remove the `()`
 
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:75:10
+  --> $DIR/unused_unit.rs:76:10
    |
 LL | fn test()->(){}
    |          ^^^^ help: remove the `-> ()`
 
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:78:11
+  --> $DIR/unused_unit.rs:79:11
    |
 LL | fn test2() ->(){}
    |           ^^^^^ help: remove the `-> ()`
 
 error: unneeded unit return type
-  --> $DIR/unused_unit.rs:81:11
+  --> $DIR/unused_unit.rs:82:11
    |
 LL | fn test3()-> (){}
    |           ^^^^^ help: remove the `-> ()`
diff --git a/src/tools/clippy/tests/versioncheck.rs b/src/tools/clippy/tests/versioncheck.rs
index f5d03c645df..589b19f68f7 100644
--- a/src/tools/clippy/tests/versioncheck.rs
+++ b/src/tools/clippy/tests/versioncheck.rs
@@ -1,3 +1,6 @@
+#![allow(clippy::single_match_else)]
+use rustc_tools_util::VersionInfo;
+
 #[test]
 fn check_that_clippy_lints_has_the_same_version_as_clippy() {
     let clippy_meta = cargo_metadata::MetadataCommand::new()
@@ -17,3 +20,53 @@ fn check_that_clippy_lints_has_the_same_version_as_clippy() {
         }
     }
 }
+
+#[test]
+fn check_that_clippy_has_the_same_major_version_as_rustc() {
+    let clippy_version = rustc_tools_util::get_version_info!();
+    let clippy_major = clippy_version.major;
+    let clippy_minor = clippy_version.minor;
+    let clippy_patch = clippy_version.patch;
+
+    // get the rustc version
+    // this way the rust-toolchain file version is honored
+    let rustc_version = String::from_utf8(
+        std::process::Command::new("rustc")
+            .arg("--version")
+            .output()
+            .expect("failed to run `rustc --version`")
+            .stdout,
+    )
+    .unwrap();
+    // extract "1 XX 0" from "rustc 1.XX.0-nightly (<commit> <date>)"
+    let vsplit: Vec<&str> = rustc_version
+        .split(' ')
+        .nth(1)
+        .unwrap()
+        .split('-')
+        .next()
+        .unwrap()
+        .split('.')
+        .collect();
+    match vsplit.as_slice() {
+        [rustc_major, rustc_minor, _rustc_patch] => {
+            // clippy 0.1.XX should correspond to rustc 1.XX.0
+            assert_eq!(clippy_major, 0); // this will probably stay the same for a long time
+            assert_eq!(
+                clippy_minor.to_string(),
+                *rustc_major,
+                "clippy minor version does not equal rustc major version"
+            );
+            assert_eq!(
+                clippy_patch.to_string(),
+                *rustc_minor,
+                "clippy patch version does not equal rustc minor version"
+            );
+            // do not check rustc_patch because when a stable-patch-release is made (like 1.50.2),
+            // we don't want our tests failing suddenly
+        },
+        _ => {
+            panic!("Failed to parse rustc version: {:?}", vsplit);
+        },
+    };
+}
diff --git a/src/tools/clippy/util/gh-pages/index.html b/src/tools/clippy/util/gh-pages/index.html
index 428708136cb..1852fb6640e 100644
--- a/src/tools/clippy/util/gh-pages/index.html
+++ b/src/tools/clippy/util/gh-pages/index.html
@@ -77,7 +77,7 @@
                     <div class="col-md-12 form-horizontal">
                         <div class="input-group">
                             <label class="input-group-addon" id="filter-label" for="filter-input">Filter:</label>
-                            <input type="text" class="form-control" placeholder="Keywords or search string" id="filter-input" ng-model="search" />
+                            <input type="text" class="form-control" placeholder="Keywords or search string" id="filter-input" ng-model="search" ng-model-options="{debounce: 50}"/>
                             <span class="input-group-btn">
                                 <button class="btn btn-default" type="button" ng-click="search = ''">
                                     Clear
@@ -119,6 +119,7 @@
                             {{title}}
                         </h4>
                         <div class="list-group-item-text" ng-bind-html="text | markdown"></div>
+                        <a ng-if="title == 'Known problems'" href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+{{lint.id}}">Search on GitHub</a>
                     </li>
                 </ul>
             </article>
@@ -180,6 +181,22 @@
             }
         }
 
+        function searchLint(lint, term) {
+            for (const field in lint.docs) {
+                // Continue if it's not a property
+                if (!lint.docs.hasOwnProperty(field)) {
+                    continue;
+                }
+
+                // Return if not found
+                if (lint.docs[field].toLowerCase().indexOf(term) !== -1) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
         angular.module("clippy", [])
         .filter('markdown', function ($sce) {
             return function (text) {
@@ -216,40 +233,31 @@
             };
 
             $scope.bySearch = function (lint, index, array) {
-                let search_str = $scope.search;
+                let searchStr = $scope.search;
                 // It can be `null` I haven't missed this value 
-                if (search_str == null || search_str.length == 0) {
+                if (searchStr == null || searchStr.length < 3) {
                     return true;
                 }
-                search_str = search_str.toLowerCase();
+                searchStr = searchStr.toLowerCase();
 
                 // Search by id
-                let id_search = search_str.trim().replace(/(\-| )/g, "_");
-                if (lint.id.includes(id_search)) {
+                if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) {
                     return true;
                 }
 
                 // Search the description
                 // The use of `for`-loops instead of `foreach` enables us to return early 
-                let search_lint = (lint, therm) => {
-                    for (const field in lint.docs) {
-                        // Continue if it's not a property
-                        if (!lint.docs.hasOwnProperty(field)) {
-                            continue;
-                        }
-
-                        // Return if not found
-                        if (lint.docs[field].toLowerCase().includes(therm)) {
-                            return true;
-                        }
+                let terms = searchStr.split(" ");
+                for (index = 0; index < terms.length; index++) {
+                    if (lint.id.indexOf(terms[index]) !== -1) {
+                        continue;
                     }
-                    return false;
-                };
-                let therms = search_str.split(" ");
-                for (index = 0; index < therms.length; index++) {
-                    if (!search_lint(lint, therms[index])) {
-                        return false;
+
+                    if (searchLint(lint, terms[index])) {
+                        continue;
                     }
+
+                    return false;
                 }
 
                 return true;