-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add initial version of const_fn lint #3648
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
c3980bf
Add initial version of const_fn lint
phansch 68cc4df
Maybe fix ICE?
phansch f9d65b6
Reorganize conditionals: Run faster checks first
phansch c0a0269
cargo fmt
phansch 0c6bdda
Use built-in entry_fn detection over self-built
phansch aed001b
Update various docs
phansch d0d7c5e
cargo fmt
phansch File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
use crate::utils::{is_entrypoint_fn, span_lint}; | ||
use rustc::hir; | ||
use rustc::hir::intravisit::FnKind; | ||
use rustc::hir::{Body, Constness, FnDecl}; | ||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; | ||
use rustc::{declare_tool_lint, lint_array}; | ||
use rustc_mir::transform::qualify_min_const_fn::is_min_const_fn; | ||
use syntax::ast::NodeId; | ||
use syntax_pos::Span; | ||
|
||
/// **What it does:** | ||
/// | ||
/// Suggests the use of `const` in functions and methods where possible. | ||
/// | ||
/// **Why is this bad?** | ||
/// | ||
/// Not having the function const prevents callers of the function from being const as well. | ||
/// | ||
/// **Known problems:** | ||
/// | ||
/// Const functions are currently still being worked on, with some features only being available | ||
/// on nightly. This lint does not consider all edge cases currently and the suggestions may be | ||
/// incorrect if you are using this lint on stable. | ||
/// | ||
/// Also, the lint only runs one pass over the code. Consider these two non-const functions: | ||
/// | ||
/// ```rust | ||
/// fn a() -> i32 { | ||
/// 0 | ||
/// } | ||
/// fn b() -> i32 { | ||
/// a() | ||
/// } | ||
/// ``` | ||
/// | ||
/// When running Clippy, the lint will only suggest to make `a` const, because `b` at this time | ||
/// can't be const as it calls a non-const function. Making `a` const and running Clippy again, | ||
/// will suggest to make `b` const, too. | ||
/// | ||
/// **Example:** | ||
/// | ||
/// ```rust | ||
/// fn new() -> Self { | ||
/// Self { random_number: 42 } | ||
/// } | ||
/// ``` | ||
/// | ||
/// Could be a const fn: | ||
/// | ||
/// ```rust | ||
/// const fn new() -> Self { | ||
/// Self { random_number: 42 } | ||
/// } | ||
/// ``` | ||
declare_clippy_lint! { | ||
pub MISSING_CONST_FOR_FN, | ||
nursery, | ||
"Lint functions definitions that could be made `const fn`" | ||
} | ||
|
||
#[derive(Clone)] | ||
pub struct MissingConstForFn; | ||
|
||
impl LintPass for MissingConstForFn { | ||
fn get_lints(&self) -> LintArray { | ||
lint_array!(MISSING_CONST_FOR_FN) | ||
} | ||
|
||
fn name(&self) -> &'static str { | ||
"MissingConstForFn" | ||
} | ||
} | ||
|
||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn { | ||
fn check_fn( | ||
&mut self, | ||
cx: &LateContext<'_, '_>, | ||
kind: FnKind<'_>, | ||
_: &FnDecl, | ||
_: &Body, | ||
span: Span, | ||
node_id: NodeId, | ||
) { | ||
let def_id = cx.tcx.hir().local_def_id(node_id); | ||
|
||
if is_entrypoint_fn(cx, def_id) { | ||
return; | ||
} | ||
|
||
// 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) { | ||
return; | ||
} | ||
}, | ||
FnKind::Method(_, sig, ..) => { | ||
if already_const(sig.header) { | ||
return; | ||
} | ||
}, | ||
_ => return, | ||
} | ||
|
||
let mir = cx.tcx.optimized_mir(def_id); | ||
|
||
if let Err((span, err)) = is_min_const_fn(cx.tcx, def_id, &mir) { | ||
if cx.tcx.is_min_const_fn(def_id) { | ||
cx.tcx.sess.span_err(span, &err); | ||
} | ||
} else { | ||
span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a const_fn"); | ||
} | ||
} | ||
} | ||
|
||
// We don't have to lint on something that's already `const` | ||
fn already_const(header: hir::FnHeader) -> bool { | ||
header.constness == Constness::Const | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
//! False-positive tests to ensure we don't suggest `const` for things where it would cause a | ||
//! compilation error. | ||
//! The .stderr output of this test should be empty. Otherwise it's a bug somewhere. | ||
|
||
#![warn(clippy::missing_const_for_fn)] | ||
#![feature(start)] | ||
|
||
struct Game; | ||
|
||
// This should not be linted because it's already const | ||
const fn already_const() -> i32 { | ||
32 | ||
} | ||
|
||
impl Game { | ||
// This should not be linted because it's already const | ||
pub const fn already_const() -> i32 { | ||
32 | ||
} | ||
} | ||
|
||
// Allowing on this function, because it would lint, which we don't want in this case. | ||
#[allow(clippy::missing_const_for_fn)] | ||
fn random() -> u32 { | ||
42 | ||
} | ||
|
||
// We should not suggest to make this function `const` because `random()` is non-const | ||
fn random_caller() -> u32 { | ||
random() | ||
} | ||
|
||
static Y: u32 = 0; | ||
|
||
// We should not suggest to make this function `const` because const functions are not allowed to | ||
// refer to a static variable | ||
fn get_y() -> u32 { | ||
Y | ||
//~^ ERROR E0013 | ||
} | ||
|
||
// Don't lint entrypoint functions | ||
#[start] | ||
fn init(num: isize, something: *const *const u8) -> isize { | ||
1 | ||
} | ||
|
||
trait Foo { | ||
// This should not be suggested to be made const | ||
// (rustc doesn't allow const trait methods) | ||
fn f() -> u32; | ||
|
||
// This should not be suggested to be made const either | ||
fn g() -> u32 { | ||
33 | ||
} | ||
} |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
#![warn(clippy::missing_const_for_fn)] | ||
#![allow(clippy::let_and_return)] | ||
|
||
use std::mem::transmute; | ||
|
||
struct Game { | ||
guess: i32, | ||
} | ||
|
||
impl Game { | ||
// Could be const | ||
pub fn new() -> Self { | ||
Self { guess: 42 } | ||
} | ||
} | ||
|
||
// Could be const | ||
fn one() -> i32 { | ||
1 | ||
} | ||
|
||
// Could also be const | ||
fn two() -> i32 { | ||
let abc = 2; | ||
abc | ||
} | ||
|
||
// FIXME: This is a false positive in the `is_min_const_fn` function. | ||
// At least until the `const_string_new` feature is stabilzed. | ||
fn string() -> String { | ||
String::new() | ||
phansch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// Could be const | ||
unsafe fn four() -> i32 { | ||
4 | ||
} | ||
|
||
// Could also be const | ||
fn generic<T>(t: T) -> T { | ||
t | ||
} | ||
|
||
// FIXME: Depends on the `const_transmute` and `const_fn` feature gates. | ||
// In the future Clippy should be able to suggest this as const, too. | ||
fn sub(x: u32) -> usize { | ||
unsafe { transmute(&x) } | ||
phansch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// NOTE: This is currently not yet allowed to be const | ||
// Once implemented, Clippy should be able to suggest this as const, too. | ||
fn generic_arr<T: Copy>(t: [T; 1]) -> T { | ||
phansch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
t[0] | ||
} | ||
|
||
// Should not be const | ||
fn main() {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
error: this could be a const_fn | ||
--> $DIR/could_be_const.rs:12:5 | ||
| | ||
LL | / pub fn new() -> Self { | ||
LL | | Self { guess: 42 } | ||
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 | ||
| | ||
LL | / fn one() -> i32 { | ||
LL | | 1 | ||
LL | | } | ||
| |_^ | ||
|
||
error: this could be a const_fn | ||
--> $DIR/could_be_const.rs:23:1 | ||
| | ||
LL | / fn two() -> i32 { | ||
LL | | let abc = 2; | ||
LL | | abc | ||
LL | | } | ||
| |_^ | ||
|
||
error: this could be a const_fn | ||
--> $DIR/could_be_const.rs:30:1 | ||
| | ||
LL | / fn string() -> String { | ||
LL | | String::new() | ||
LL | | } | ||
| |_^ | ||
|
||
error: this could be a const_fn | ||
--> $DIR/could_be_const.rs:35:1 | ||
| | ||
LL | / unsafe fn four() -> i32 { | ||
LL | | 4 | ||
LL | | } | ||
| |_^ | ||
|
||
error: this could be a const_fn | ||
--> $DIR/could_be_const.rs:40:1 | ||
| | ||
LL | / fn generic<T>(t: T) -> T { | ||
LL | | t | ||
LL | | } | ||
| |_^ | ||
|
||
error: aborting due to 6 previous errors | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.