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