Skip to content

Add lint to replace consts with const fns #2344

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 1 commit into from
Jan 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ pub mod ptr;
pub mod ranges;
pub mod reference;
pub mod regex;
pub mod replace_consts;
pub mod returns;
pub mod serde_api;
pub mod shadow;
Expand Down Expand Up @@ -361,6 +362,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
reg.register_late_lint_pass(box types::ImplicitHasher);
reg.register_early_lint_pass(box const_static_lifetime::StaticConst);
reg.register_late_lint_pass(box fallible_impl_from::FallibleImplFrom);
reg.register_late_lint_pass(box replace_consts::ReplaceConsts);

reg.register_lint_group("clippy_restrictions", vec![
arithmetic::FLOAT_ARITHMETIC,
Expand Down Expand Up @@ -399,6 +401,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
print::PRINT_STDOUT,
print::USE_DEBUG,
ranges::RANGE_PLUS_ONE,
replace_consts::REPLACE_CONSTS,
shadow::SHADOW_REUSE,
shadow::SHADOW_SAME,
shadow::SHADOW_UNRELATED,
Expand Down
102 changes: 102 additions & 0 deletions clippy_lints/src/replace_consts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use rustc::lint::*;
use rustc::hir;
use rustc::hir::def::Def;
use utils::{match_def_path, span_lint_and_sugg};

/// **What it does:** Checks for usage of `ATOMIC_X_INIT`, `ONCE_INIT`, and
/// `uX/iX::MIN/MAX`.
///
/// **Why is this bad?** `const fn`s exist
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// static FOO: AtomicIsize = ATOMIC_ISIZE_INIT;
/// ```
///
/// Could be written:
///
/// ```rust
/// static FOO: AtomicIsize = AtomicIsize::new(0);
/// ```
declare_lint! {
pub REPLACE_CONSTS,
Allow,
"Lint usages of standard library `const`s that could be replaced by `const fn`s"
}

pub struct ReplaceConsts;

impl LintPass for ReplaceConsts {
fn get_lints(&self) -> LintArray {
lint_array!(REPLACE_CONSTS)
}
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ReplaceConsts {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
if_chain! {
if let hir::ExprPath(ref qp) = expr.node;
if let Def::Const(def_id) = cx.tables.qpath_def(qp, expr.hir_id);
then {
for &(const_path, repl_snip) in REPLACEMENTS {
if match_def_path(cx.tcx, def_id, const_path) {
span_lint_and_sugg(
cx,
REPLACE_CONSTS,
expr.span,
&format!("using `{}`", const_path.last().expect("empty path")),
"try this",
repl_snip.to_string(),
);
return;
}
}
}
}
}
}

const REPLACEMENTS: &[(&[&str], &str)] = &[
// Once
(&["core", "sync", "ONCE_INIT"], "Once::new()"),
// Atomic
(&["core", "sync", "atomic", "ATOMIC_BOOL_INIT"], "AtomicBool::new(false)"),
(&["core", "sync", "atomic", "ATOMIC_ISIZE_INIT"], "AtomicIsize::new(0)"),
(&["core", "sync", "atomic", "ATOMIC_I8_INIT"], "AtomicI8::new(0)"),
(&["core", "sync", "atomic", "ATOMIC_I16_INIT"], "AtomicI16::new(0)"),
(&["core", "sync", "atomic", "ATOMIC_I32_INIT"], "AtomicI32::new(0)"),
(&["core", "sync", "atomic", "ATOMIC_I64_INIT"], "AtomicI64::new(0)"),
(&["core", "sync", "atomic", "ATOMIC_USIZE_INIT"], "AtomicUsize::new(0)"),
(&["core", "sync", "atomic", "ATOMIC_U8_INIT"], "AtomicU8::new(0)"),
(&["core", "sync", "atomic", "ATOMIC_U16_INIT"], "AtomicU16::new(0)"),
(&["core", "sync", "atomic", "ATOMIC_U32_INIT"], "AtomicU32::new(0)"),
(&["core", "sync", "atomic", "ATOMIC_U64_INIT"], "AtomicU64::new(0)"),
// Min
(&["core", "isize", "MIN"], "isize::min_value()"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MIN/MAX are not bad at all, I'd rather recommend the opposite replacement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a config option for enabling MIN/MAX on this lint. The opposite replacement might be better suited for a different lint though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@petrochenkov why? the MIN is in std::isize::MIN and thus requires the full path or an import. min_value exists on the type

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's actually the main reason I recommend these; fewer paths to import.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I sort of assumed that it'd make sense to long-term push people toward the const fn versions over the constants. For the INIT structs, they simply aren't at clear as using new, and for the MIN/MAX structs, the types themselves are already in scope.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the MIN is in std::isize::MIN and thus requires the full path or an import.

Oh. I thought they are associated constants already.
Someone should resurrect that PR.

I sort of assumed that it'd make sense to long-term push people toward the const fn versions over the constants.

Why? Constants exist in the language to represent... constants. MIN/MAX are constants, why would you avoid a feature specifically designed to represent them (except for the full path issue which is a fixable library deficiency). min_value() is longer and has that extraneous call.
(I agree about INIT atomics though.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^^ This was a concern about constants and const functions in general.
All the listed constants have specific reasons to avoid them (until rust-lang/rust#28656 is redone), so this PR is okay.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally agree with you @petrochenkov in terms of making them associated constants. When you do end up redoing that PR, you should include the float constants too, as those don't have associated const fns.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, no config option for MIN/MAX then?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, leave it out, we'll adjust the lint when it makes sense to behave differently on stable

(&["core", "i8", "MIN"], "i8::min_value()"),
(&["core", "i16", "MIN"], "i16::min_value()"),
(&["core", "i32", "MIN"], "i32::min_value()"),
(&["core", "i64", "MIN"], "i64::min_value()"),
(&["core", "i128", "MIN"], "i128::min_value()"),
(&["core", "usize", "MIN"], "usize::min_value()"),
(&["core", "u8", "MIN"], "u8::min_value()"),
(&["core", "u16", "MIN"], "u16::min_value()"),
(&["core", "u32", "MIN"], "u32::min_value()"),
(&["core", "u64", "MIN"], "u64::min_value()"),
(&["core", "u128", "MIN"], "u128::min_value()"),
// Max
(&["core", "isize", "MAX"], "isize::max_value()"),
(&["core", "i8", "MAX"], "i8::max_value()"),
(&["core", "i16", "MAX"], "i16::max_value()"),
(&["core", "i32", "MAX"], "i32::max_value()"),
(&["core", "i64", "MAX"], "i64::max_value()"),
(&["core", "i128", "MAX"], "i128::max_value()"),
(&["core", "usize", "MAX"], "usize::max_value()"),
(&["core", "u8", "MAX"], "u8::max_value()"),
(&["core", "u16", "MAX"], "u16::max_value()"),
(&["core", "u32", "MAX"], "u32::max_value()"),
(&["core", "u64", "MAX"], "u64::max_value()"),
(&["core", "u128", "MAX"], "u128::max_value()"),
];
96 changes: 96 additions & 0 deletions tests/ui/replace_consts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#![feature(integer_atomics, i128, i128_type)]
#![allow(blacklisted_name)]
#![deny(replace_consts)]
use std::sync::atomic::*;
use std::sync::{ONCE_INIT, Once};

fn bad() {
// Once
{ let foo = ONCE_INIT; };
// Atomic
{ let foo = ATOMIC_BOOL_INIT; };
{ let foo = ATOMIC_ISIZE_INIT; };
{ let foo = ATOMIC_I8_INIT; };
{ let foo = ATOMIC_I16_INIT; };
{ let foo = ATOMIC_I32_INIT; };
{ let foo = ATOMIC_I64_INIT; };
{ let foo = ATOMIC_USIZE_INIT; };
{ let foo = ATOMIC_U8_INIT; };
{ let foo = ATOMIC_U16_INIT; };
{ let foo = ATOMIC_U32_INIT; };
{ let foo = ATOMIC_U64_INIT; };
// Min
{ let foo = std::isize::MIN; };
{ let foo = std::i8::MIN; };
{ let foo = std::i16::MIN; };
{ let foo = std::i32::MIN; };
{ let foo = std::i64::MIN; };
{ let foo = std::i128::MIN; };
{ let foo = std::usize::MIN; };
{ let foo = std::u8::MIN; };
{ let foo = std::u16::MIN; };
{ let foo = std::u32::MIN; };
{ let foo = std::u64::MIN; };
{ let foo = std::u128::MIN; };
// Max
{ let foo = std::isize::MAX; };
{ let foo = std::i8::MAX; };
{ let foo = std::i16::MAX; };
{ let foo = std::i32::MAX; };
{ let foo = std::i64::MAX; };
{ let foo = std::i128::MAX; };
{ let foo = std::usize::MAX; };
{ let foo = std::u8::MAX; };
{ let foo = std::u16::MAX; };
{ let foo = std::u32::MAX; };
{ let foo = std::u64::MAX; };
{ let foo = std::u128::MAX; };
}

fn good() {
// Once
{ let foo = Once::new(); };
// Atomic
{ let foo = AtomicBool::new(false); };
{ let foo = AtomicIsize::new(0); };
{ let foo = AtomicI8::new(0); };
{ let foo = AtomicI16::new(0); };
{ let foo = AtomicI32::new(0); };
{ let foo = AtomicI64::new(0); };
{ let foo = AtomicUsize::new(0); };
{ let foo = AtomicU8::new(0); };
{ let foo = AtomicU16::new(0); };
{ let foo = AtomicU32::new(0); };
{ let foo = AtomicU64::new(0); };
// Min
{ let foo = isize::min_value(); };
{ let foo = i8::min_value(); };
{ let foo = i16::min_value(); };
{ let foo = i32::min_value(); };
{ let foo = i64::min_value(); };
{ let foo = i128::min_value(); };
{ let foo = usize::min_value(); };
{ let foo = u8::min_value(); };
{ let foo = u16::min_value(); };
{ let foo = u32::min_value(); };
{ let foo = u64::min_value(); };
{ let foo = u128::min_value(); };
// Max
{ let foo = isize::max_value(); };
{ let foo = i8::max_value(); };
{ let foo = i16::max_value(); };
{ let foo = i32::max_value(); };
{ let foo = i64::max_value(); };
{ let foo = i128::max_value(); };
{ let foo = usize::max_value(); };
{ let foo = u8::max_value(); };
{ let foo = u16::max_value(); };
{ let foo = u32::max_value(); };
{ let foo = u64::max_value(); };
{ let foo = u128::max_value(); };
}

fn main() {
bad();
good();
}
Loading