From 049079856bb300c9b00a1ddbf4b58d0a5ca40309 Mon Sep 17 00:00:00 2001
From: Krishna Sai Veera Reddy <veerareddy@email.arizona.edu>
Date: Fri, 21 Feb 2020 19:52:04 -0800
Subject: [PATCH] Prevent `missing_const_for_fn` on functions with const
 generic params

`const` functions cannot have const generic parameters so prevent the
`missing_const_for_fn` lint from firing in that case.
---
 clippy_lints/src/missing_const_for_fn.rs      | 12 ++++++---
 .../ui/missing_const_for_fn/cant_be_const.rs  | 13 +++++++++-
 .../ui/missing_const_for_fn/could_be_const.rs |  7 ++++-
 .../could_be_const.stderr                     | 26 ++++++++++++-------
 4 files changed, 44 insertions(+), 14 deletions(-)

diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs
index dc5a58abe56..e829baa2268 100644
--- a/clippy_lints/src/missing_const_for_fn.rs
+++ b/clippy_lints/src/missing_const_for_fn.rs
@@ -2,12 +2,13 @@ use crate::utils::{has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method};
 use rustc::lint::in_external_macro;
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
-use rustc_hir::{Body, Constness, FnDecl, HirId};
+use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_mir::transform::qualify_min_const_fn::is_min_const_fn;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::Span;
 use rustc_typeck::hir_ty_to_ty;
+use std::matches;
 
 declare_clippy_lint! {
     /// **What it does:**
@@ -90,8 +91,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn {
         // Perform some preliminary checks that rule out constness on the Clippy side. This way we
         // can skip the actual const check and return early.
         match kind {
-            FnKind::ItemFn(_, _, header, ..) => {
-                if already_const(header) {
+            FnKind::ItemFn(_, generics, header, ..) => {
+                let has_const_generic_params = generics
+                    .params
+                    .iter()
+                    .any(|param| matches!(param.kind, GenericParamKind::Const{ .. }));
+
+                if already_const(header) || has_const_generic_params {
                     return;
                 }
             },
diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs
index f367279906f..ba352ef9ee9 100644
--- a/tests/ui/missing_const_for_fn/cant_be_const.rs
+++ b/tests/ui/missing_const_for_fn/cant_be_const.rs
@@ -3,7 +3,8 @@
 //! The .stderr output of this test should be empty. Otherwise it's a bug somewhere.
 
 #![warn(clippy::missing_const_for_fn)]
-#![feature(start)]
+#![allow(incomplete_features)]
+#![feature(start, const_generics)]
 
 struct Game;
 
@@ -90,3 +91,13 @@ mod with_drop {
         }
     }
 }
+
+fn const_generic_params<T, const N: usize>(t: &[T; N]) -> &[T; N] {
+    t
+}
+
+fn const_generic_return<T, const N: usize>(t: &[T]) -> &[T; N] {
+    let p = t.as_ptr() as *const [T; N];
+
+    unsafe { &*p }
+}
diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs
index 9a73dcbe99f..c6f44b7daa3 100644
--- a/tests/ui/missing_const_for_fn/could_be_const.rs
+++ b/tests/ui/missing_const_for_fn/could_be_const.rs
@@ -1,5 +1,6 @@
 #![warn(clippy::missing_const_for_fn)]
-#![allow(clippy::let_and_return)]
+#![allow(incomplete_features, clippy::let_and_return)]
+#![feature(const_generics)]
 
 use std::mem::transmute;
 
@@ -12,6 +13,10 @@ impl Game {
     pub fn new() -> Self {
         Self { guess: 42 }
     }
+
+    fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
+        b
+    }
 }
 
 // Could be const
diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr
index 165b8a03077..8dde56cd79f 100644
--- a/tests/ui/missing_const_for_fn/could_be_const.stderr
+++ b/tests/ui/missing_const_for_fn/could_be_const.stderr
@@ -1,5 +1,5 @@
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:12:5
+  --> $DIR/could_be_const.rs:13:5
    |
 LL | /     pub fn new() -> Self {
 LL | |         Self { guess: 42 }
@@ -9,7 +9,15 @@ LL | |     }
    = note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:18:1
+  --> $DIR/could_be_const.rs:17:5
+   |
+LL | /     fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
+LL | |         b
+LL | |     }
+   | |_____^
+
+error: this could be a `const fn`
+  --> $DIR/could_be_const.rs:23:1
    |
 LL | / fn one() -> i32 {
 LL | |     1
@@ -17,7 +25,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:23:1
+  --> $DIR/could_be_const.rs:28:1
    |
 LL | / fn two() -> i32 {
 LL | |     let abc = 2;
@@ -26,7 +34,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:29:1
+  --> $DIR/could_be_const.rs:34:1
    |
 LL | / fn string() -> String {
 LL | |     String::new()
@@ -34,7 +42,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:34:1
+  --> $DIR/could_be_const.rs:39:1
    |
 LL | / unsafe fn four() -> i32 {
 LL | |     4
@@ -42,7 +50,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:39:1
+  --> $DIR/could_be_const.rs:44:1
    |
 LL | / fn generic<T>(t: T) -> T {
 LL | |     t
@@ -50,7 +58,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:43:1
+  --> $DIR/could_be_const.rs:48:1
    |
 LL | / fn sub(x: u32) -> usize {
 LL | |     unsafe { transmute(&x) }
@@ -58,12 +66,12 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:62:9
+  --> $DIR/could_be_const.rs:67:9
    |
 LL | /         pub fn b(self, a: &A) -> B {
 LL | |             B
 LL | |         }
    | |_________^
 
-error: aborting due to 8 previous errors
+error: aborting due to 9 previous errors