|
| 1 | +use rustc::hir; |
| 2 | +use rustc::hir::{Body, FnDecl, Constness}; |
| 3 | +use rustc::hir::intravisit::FnKind; |
| 4 | +// use rustc::mir::*; |
| 5 | +use syntax::ast::{NodeId, Attribute}; |
| 6 | +use syntax_pos::Span; |
| 7 | +use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; |
| 8 | +use rustc::{declare_tool_lint, lint_array}; |
| 9 | +use rustc_mir::transform::qualify_min_const_fn::is_min_const_fn; |
| 10 | +use crate::utils::{span_lint, is_entrypoint_fn}; |
| 11 | + |
| 12 | +/// **What it does:** |
| 13 | +/// |
| 14 | +/// Suggests the use of `const` in functions and methods where possible |
| 15 | +/// |
| 16 | +/// **Why is this bad?** |
| 17 | +/// Not using `const` is a missed optimization. Instead of having the function execute at runtime, |
| 18 | +/// when using `const`, it's evaluated at compiletime. |
| 19 | +/// |
| 20 | +/// **Known problems:** |
| 21 | +/// |
| 22 | +/// Const functions are currently still being worked on, with some features only being available |
| 23 | +/// on nightly. This lint does not consider all edge cases currently and the suggestions may be |
| 24 | +/// incorrect if you are using this lint on stable. |
| 25 | +/// |
| 26 | +/// Also, the lint only runs one pass over the code. Consider these two non-const functions: |
| 27 | +/// |
| 28 | +/// ```rust |
| 29 | +/// fn a() -> i32 { 0 } |
| 30 | +/// fn b() -> i32 { a() } |
| 31 | +/// ``` |
| 32 | +/// |
| 33 | +/// When running Clippy, the lint will only suggest to make `a` const, because `b` at this time |
| 34 | +/// can't be const as it calls a non-const function. Making `a` const and running Clippy again, |
| 35 | +/// will suggest to make `b` const, too. |
| 36 | +/// |
| 37 | +/// **Example:** |
| 38 | +/// |
| 39 | +/// ```rust |
| 40 | +/// fn new() -> Self { |
| 41 | +/// Self { |
| 42 | +/// random_number: 42 |
| 43 | +/// } |
| 44 | +/// } |
| 45 | +/// ``` |
| 46 | +/// |
| 47 | +/// Could be a const fn: |
| 48 | +/// |
| 49 | +/// ```rust |
| 50 | +/// const fn new() -> Self { |
| 51 | +/// Self { |
| 52 | +/// random_number: 42 |
| 53 | +/// } |
| 54 | +/// } |
| 55 | +/// ``` |
| 56 | +declare_clippy_lint! { |
| 57 | + pub MISSING_CONST_FOR_FN, |
| 58 | + nursery, |
| 59 | + "Lint functions definitions that could be made `const fn`" |
| 60 | +} |
| 61 | + |
| 62 | +#[derive(Clone)] |
| 63 | +pub struct MissingConstForFn; |
| 64 | + |
| 65 | +impl LintPass for MissingConstForFn { |
| 66 | + fn get_lints(&self) -> LintArray { |
| 67 | + lint_array!(MISSING_CONST_FOR_FN) |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn { |
| 72 | + fn check_fn( |
| 73 | + &mut self, |
| 74 | + cx: &LateContext<'_, '_>, |
| 75 | + kind: FnKind<'_>, |
| 76 | + _: &FnDecl, |
| 77 | + _: &Body, |
| 78 | + span: Span, |
| 79 | + node_id: NodeId |
| 80 | + ) { |
| 81 | + let def_id = cx.tcx.hir().local_def_id(node_id); |
| 82 | + let mir = cx.tcx.optimized_mir(def_id); |
| 83 | + if let Err((span, err) = is_min_const_fn(cx.tcx, def_id, &mir) { |
| 84 | + cx.tcx.sess.span_err(span, &err); |
| 85 | + } else { |
| 86 | + match kind { |
| 87 | + FnKind::ItemFn(name, _generics, header, _vis, attrs) => { |
| 88 | + if !can_be_const_fn(&name.as_str(), header, attrs) { |
| 89 | + return; |
| 90 | + } |
| 91 | + }, |
| 92 | + FnKind::Method(ident, sig, _vis, attrs) => { |
| 93 | + let header = sig.header; |
| 94 | + let name = ident.name.as_str(); |
| 95 | + if !can_be_const_fn(&name, header, attrs) { |
| 96 | + return; |
| 97 | + } |
| 98 | + }, |
| 99 | + _ => return |
| 100 | + } |
| 101 | + span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a const_fn"); |
| 102 | + } |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +fn can_be_const_fn(name: &str, header: hir::FnHeader, attrs: &[Attribute]) -> bool { |
| 107 | + // Main and custom entrypoints can't be `const` |
| 108 | + if is_entrypoint_fn(name, attrs) { return false } |
| 109 | + |
| 110 | + // We don't have to lint on something that's already `const` |
| 111 | + if header.constness == Constness::Const { return false } |
| 112 | + true |
| 113 | +} |
0 commit comments