diff options
Diffstat (limited to 'clippy_lints/src')
| -rw-r--r-- | clippy_lints/src/lib.rs | 5 | ||||
| -rw-r--r-- | clippy_lints/src/missing_const_for_fn.rs | 121 | ||||
| -rw-r--r-- | clippy_lints/src/utils/mod.rs | 10 |
3 files changed, 135 insertions, 1 deletions
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4683d353ccf..3483aae0ca3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -23,6 +23,8 @@ extern crate rustc_data_structures; #[allow(unused_extern_crates)] extern crate rustc_errors; #[allow(unused_extern_crates)] +extern crate rustc_mir; +#[allow(unused_extern_crates)] extern crate rustc_plugin; #[allow(unused_extern_crates)] extern crate rustc_target; @@ -144,6 +146,7 @@ pub mod methods; pub mod minmax; pub mod misc; pub mod misc_early; +pub mod missing_const_for_fn; pub mod missing_doc; pub mod missing_inline; pub mod multiple_crate_versions; @@ -486,6 +489,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { reg.register_late_lint_pass(box slow_vector_initialization::Pass); reg.register_late_lint_pass(box types::RefToMut); reg.register_late_lint_pass(box assertions_on_constants::AssertionsOnConstants); + reg.register_late_lint_pass(box missing_const_for_fn::MissingConstForFn); reg.register_lint_group("clippy::restriction", Some("clippy_restriction"), vec![ arithmetic::FLOAT_ARITHMETIC, @@ -1027,6 +1031,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { reg.register_lint_group("clippy::nursery", Some("clippy_nursery"), vec![ attrs::EMPTY_LINE_AFTER_OUTER_ATTR, fallible_impl_from::FALLIBLE_IMPL_FROM, + missing_const_for_fn::MISSING_CONST_FOR_FN, mutex_atomic::MUTEX_INTEGER, needless_borrow::NEEDLESS_BORROW, redundant_clone::REDUNDANT_CLONE, diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs new file mode 100644 index 00000000000..9228c586bbf --- /dev/null +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -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 +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 2b2b3e8b2f6..ee3356fdc82 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -3,7 +3,7 @@ use if_chain::if_chain; use matches::matches; use rustc::hir; use rustc::hir::def::Def; -use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX}; +use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc::hir::intravisit::{NestedVisitorMap, Visitor}; use rustc::hir::Node; use rustc::hir::*; @@ -350,6 +350,14 @@ pub fn method_chain_args<'a>(expr: &'a Expr, methods: &[&str]) -> Option<Vec<&'a Some(matched) } +/// Returns true if the provided `def_id` is an entrypoint to a program +pub fn is_entrypoint_fn(cx: &LateContext<'_, '_>, def_id: DefId) -> bool { + if let Some((entry_fn_def_id, _)) = cx.tcx.entry_fn(LOCAL_CRATE) { + return def_id == entry_fn_def_id; + } + false +} + /// Get the name of the item the expression is in, if available. pub fn get_item_name(cx: &LateContext<'_, '_>, expr: &Expr) -> Option<Name> { let parent_id = cx.tcx.hir().get_parent(expr.id); |
