-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add initial version of const_fn lint
- Loading branch information
Showing
9 changed files
with
302 additions
and
1 deletion.
There are no files selected for viewing
This file contains 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 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 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 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,113 @@ | ||
use rustc::hir; | ||
use rustc::hir::{Body, FnDecl, Constness}; | ||
use rustc::hir::intravisit::FnKind; | ||
// use rustc::mir::*; | ||
use syntax::ast::{NodeId, Attribute}; | ||
use syntax_pos::Span; | ||
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 crate::utils::{span_lint, is_entrypoint_fn}; | ||
|
||
/// **What it does:** | ||
/// | ||
/// Suggests the use of `const` in functions and methods where possible | ||
/// | ||
/// **Why is this bad?** | ||
/// Not using `const` is a missed optimization. Instead of having the function execute at runtime, | ||
/// when using `const`, it's evaluated at compiletime. | ||
/// `const fn`s exist | ||
/// TODO: Explain why const is preferable over non-const | ||
/// | ||
/// **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. | ||
/// | ||
/// 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) | ||
} | ||
} | ||
|
||
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); | ||
let mir = cx.tcx.optimized_mir(def_id); | ||
if let Ok(_) = is_min_const_fn(cx.tcx, def_id, &mir) { | ||
match kind { | ||
FnKind::ItemFn(name, _generics, header, _vis, attrs) => { | ||
if !can_be_const_fn(&name.as_str(), header, attrs) { | ||
return; | ||
} | ||
}, | ||
FnKind::Method(ident, sig, _vis, attrs) => { | ||
let header = sig.header; | ||
let name = ident.name.as_str(); | ||
if !can_be_const_fn(&name, header, attrs) { | ||
return; | ||
} | ||
}, | ||
_ => return | ||
} | ||
span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a const_fn"); | ||
} | ||
} | ||
} | ||
|
||
fn can_be_const_fn(name: &str, header: hir::FnHeader, attrs: &[Attribute]) -> bool { | ||
// Main and custom entrypoints can't be `const` | ||
if is_entrypoint_fn(name, attrs) { return false } | ||
|
||
// We don't have to lint on something that's already `const` | ||
if header.constness == Constness::Const { return false } | ||
true | ||
} |
This file contains 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 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,50 @@ | ||
//! 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 | ||
} | ||
|
||
// Also main should not be suggested to be made const | ||
fn main() { | ||
// We should also be sure to not lint on closures | ||
let add_one_v2 = |x: u32| -> u32 { x + 1 }; | ||
} | ||
|
||
trait Foo { | ||
// This should not be suggested to be made const | ||
// (rustc restriction) | ||
fn f() -> u32; | ||
} | ||
|
||
// Don't lint custom entrypoints either | ||
#[start] | ||
fn init(num: isize, something: *const *const u8) -> isize { 1 } |
This file contains 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,24 @@ | ||
error: extern items cannot be `const` | ||
--> $DIR/cant_be_const.rs:44:5 | ||
| | ||
LL | const fn abc() -> i32 { 32 } | ||
| ^^^^^ help: try using a static value: `static` | ||
|
||
error: expected identifier, found keyword `fn` | ||
--> $DIR/cant_be_const.rs:44:11 | ||
| | ||
LL | const fn abc() -> i32 { 32 } | ||
| ^^ expected identifier, found keyword | ||
help: you can escape reserved keywords to use them as identifiers | ||
| | ||
LL | const r#fn abc() -> i32 { 32 } | ||
| ^^^^ | ||
|
||
error: expected `:`, found `abc` | ||
--> $DIR/cant_be_const.rs:44:14 | ||
| | ||
LL | const fn abc() -> i32 { 32 } | ||
| ^^^ expected `:` | ||
|
||
error: aborting due to 3 previous errors | ||
|
This file contains 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 @@ | ||
#![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 | ||
} | ||
|
||
// TODO: Why can this be const? because it's a zero sized type? | ||
// There is the `const_string_new` feature, but it seems that this already works in const fns? | ||
fn string() -> String { | ||
String::new() | ||
} | ||
|
||
// Could be const | ||
unsafe fn four() -> i32 { 4 } | ||
|
||
// Could also be const | ||
fn generic<T>(t: T) -> T { | ||
t | ||
} | ||
|
||
// FIXME: This could be const but is currently not linted | ||
fn sub(x: u32) -> usize { | ||
unsafe { transmute(&x) } | ||
} | ||
|
||
// FIXME: This could be const but is currently not linted | ||
fn generic_arr<T: Copy>(t: [T; 1]) -> T { | ||
t[0] | ||
} | ||
|
||
// Should not be const | ||
fn main() {} |
This file contains 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,42 @@ | ||
error: this could be a const_fn | ||
--> $DIR/could_be_const.rs:15:5 | ||
| | ||
LL | / pub fn new() -> Self { | ||
LL | | Self { | ||
LL | | guess: 42, | ||
LL | | } | ||
LL | | } | ||
| |_____^ | ||
| | ||
= note: `-D clippy::missing-const-for-fn` implied by `-D warnings` | ||
|
||
error: this could be a const_fn | ||
--> $DIR/could_be_const.rs:23:1 | ||
| | ||
LL | fn one() -> i32 { 1 } | ||
| ^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: this could be a const_fn | ||
--> $DIR/could_be_const.rs:33:1 | ||
| | ||
LL | / fn string() -> String { | ||
LL | | String::new() | ||
LL | | } | ||
| |_^ | ||
|
||
error: this could be a const_fn | ||
--> $DIR/could_be_const.rs:38:1 | ||
| | ||
LL | unsafe fn four() -> i32 { 4 } | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: this could be a const_fn | ||
--> $DIR/could_be_const.rs:41:1 | ||
| | ||
LL | / fn generic<T>(t: T) -> T { | ||
LL | | t | ||
LL | | } | ||
| |_^ | ||
|
||
error: aborting due to 5 previous errors | ||
|