|
| 1 | +mod must_use; |
| 2 | +mod not_unsafe_ptr_arg_deref; |
| 3 | +mod result_unit_err; |
| 4 | +mod too_many_arguments; |
| 5 | +mod too_many_lines; |
| 6 | + |
| 7 | +use rustc_hir as hir; |
| 8 | +use rustc_hir::intravisit; |
| 9 | +use rustc_lint::{LateContext, LateLintPass}; |
| 10 | +use rustc_session::{declare_tool_lint, impl_lint_pass}; |
| 11 | +use rustc_span::Span; |
| 12 | + |
| 13 | +declare_clippy_lint! { |
| 14 | + /// **What it does:** Checks for functions with too many parameters. |
| 15 | + /// |
| 16 | + /// **Why is this bad?** Functions with lots of parameters are considered bad |
| 17 | + /// style and reduce readability (“what does the 5th parameter mean?”). Consider |
| 18 | + /// grouping some parameters into a new type. |
| 19 | + /// |
| 20 | + /// **Known problems:** None. |
| 21 | + /// |
| 22 | + /// **Example:** |
| 23 | + /// ```rust |
| 24 | + /// # struct Color; |
| 25 | + /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) { |
| 26 | + /// // .. |
| 27 | + /// } |
| 28 | + /// ``` |
| 29 | + pub TOO_MANY_ARGUMENTS, |
| 30 | + complexity, |
| 31 | + "functions with too many arguments" |
| 32 | +} |
| 33 | + |
| 34 | +declare_clippy_lint! { |
| 35 | + /// **What it does:** Checks for functions with a large amount of lines. |
| 36 | + /// |
| 37 | + /// **Why is this bad?** Functions with a lot of lines are harder to understand |
| 38 | + /// due to having to look at a larger amount of code to understand what the |
| 39 | + /// function is doing. Consider splitting the body of the function into |
| 40 | + /// multiple functions. |
| 41 | + /// |
| 42 | + /// **Known problems:** None. |
| 43 | + /// |
| 44 | + /// **Example:** |
| 45 | + /// ```rust |
| 46 | + /// fn im_too_long() { |
| 47 | + /// println!(""); |
| 48 | + /// // ... 100 more LoC |
| 49 | + /// println!(""); |
| 50 | + /// } |
| 51 | + /// ``` |
| 52 | + pub TOO_MANY_LINES, |
| 53 | + pedantic, |
| 54 | + "functions with too many lines" |
| 55 | +} |
| 56 | + |
| 57 | +declare_clippy_lint! { |
| 58 | + /// **What it does:** Checks for public functions that dereference raw pointer |
| 59 | + /// arguments but are not marked `unsafe`. |
| 60 | + /// |
| 61 | + /// **Why is this bad?** The function should probably be marked `unsafe`, since |
| 62 | + /// for an arbitrary raw pointer, there is no way of telling for sure if it is |
| 63 | + /// valid. |
| 64 | + /// |
| 65 | + /// **Known problems:** |
| 66 | + /// |
| 67 | + /// * It does not check functions recursively so if the pointer is passed to a |
| 68 | + /// private non-`unsafe` function which does the dereferencing, the lint won't |
| 69 | + /// trigger. |
| 70 | + /// * It only checks for arguments whose type are raw pointers, not raw pointers |
| 71 | + /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or |
| 72 | + /// `some_argument.get_raw_ptr()`). |
| 73 | + /// |
| 74 | + /// **Example:** |
| 75 | + /// ```rust,ignore |
| 76 | + /// // Bad |
| 77 | + /// pub fn foo(x: *const u8) { |
| 78 | + /// println!("{}", unsafe { *x }); |
| 79 | + /// } |
| 80 | + /// |
| 81 | + /// // Good |
| 82 | + /// pub unsafe fn foo(x: *const u8) { |
| 83 | + /// println!("{}", unsafe { *x }); |
| 84 | + /// } |
| 85 | + /// ``` |
| 86 | + pub NOT_UNSAFE_PTR_ARG_DEREF, |
| 87 | + correctness, |
| 88 | + "public functions dereferencing raw pointer arguments but not marked `unsafe`" |
| 89 | +} |
| 90 | + |
| 91 | +declare_clippy_lint! { |
| 92 | + /// **What it does:** Checks for a [`#[must_use]`] attribute on |
| 93 | + /// unit-returning functions and methods. |
| 94 | + /// |
| 95 | + /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute |
| 96 | + /// |
| 97 | + /// **Why is this bad?** Unit values are useless. The attribute is likely |
| 98 | + /// a remnant of a refactoring that removed the return type. |
| 99 | + /// |
| 100 | + /// **Known problems:** None. |
| 101 | + /// |
| 102 | + /// **Examples:** |
| 103 | + /// ```rust |
| 104 | + /// #[must_use] |
| 105 | + /// fn useless() { } |
| 106 | + /// ``` |
| 107 | + pub MUST_USE_UNIT, |
| 108 | + style, |
| 109 | + "`#[must_use]` attribute on a unit-returning function / method" |
| 110 | +} |
| 111 | + |
| 112 | +declare_clippy_lint! { |
| 113 | + /// **What it does:** Checks for a [`#[must_use]`] attribute without |
| 114 | + /// further information on functions and methods that return a type already |
| 115 | + /// marked as `#[must_use]`. |
| 116 | + /// |
| 117 | + /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute |
| 118 | + /// |
| 119 | + /// **Why is this bad?** The attribute isn't needed. Not using the result |
| 120 | + /// will already be reported. Alternatively, one can add some text to the |
| 121 | + /// attribute to improve the lint message. |
| 122 | + /// |
| 123 | + /// **Known problems:** None. |
| 124 | + /// |
| 125 | + /// **Examples:** |
| 126 | + /// ```rust |
| 127 | + /// #[must_use] |
| 128 | + /// fn double_must_use() -> Result<(), ()> { |
| 129 | + /// unimplemented!(); |
| 130 | + /// } |
| 131 | + /// ``` |
| 132 | + pub DOUBLE_MUST_USE, |
| 133 | + style, |
| 134 | + "`#[must_use]` attribute on a `#[must_use]`-returning function / method" |
| 135 | +} |
| 136 | + |
| 137 | +declare_clippy_lint! { |
| 138 | + /// **What it does:** Checks for public functions that have no |
| 139 | + /// [`#[must_use]`] attribute, but return something not already marked |
| 140 | + /// must-use, have no mutable arg and mutate no statics. |
| 141 | + /// |
| 142 | + /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute |
| 143 | + /// |
| 144 | + /// **Why is this bad?** Not bad at all, this lint just shows places where |
| 145 | + /// you could add the attribute. |
| 146 | + /// |
| 147 | + /// **Known problems:** The lint only checks the arguments for mutable |
| 148 | + /// types without looking if they are actually changed. On the other hand, |
| 149 | + /// it also ignores a broad range of potentially interesting side effects, |
| 150 | + /// because we cannot decide whether the programmer intends the function to |
| 151 | + /// be called for the side effect or the result. Expect many false |
| 152 | + /// positives. At least we don't lint if the result type is unit or already |
| 153 | + /// `#[must_use]`. |
| 154 | + /// |
| 155 | + /// **Examples:** |
| 156 | + /// ```rust |
| 157 | + /// // this could be annotated with `#[must_use]`. |
| 158 | + /// fn id<T>(t: T) -> T { t } |
| 159 | + /// ``` |
| 160 | + pub MUST_USE_CANDIDATE, |
| 161 | + pedantic, |
| 162 | + "function or method that could take a `#[must_use]` attribute" |
| 163 | +} |
| 164 | + |
| 165 | +declare_clippy_lint! { |
| 166 | + /// **What it does:** Checks for public functions that return a `Result` |
| 167 | + /// with an `Err` type of `()`. It suggests using a custom type that |
| 168 | + /// implements `std::error::Error`. |
| 169 | + /// |
| 170 | + /// **Why is this bad?** Unit does not implement `Error` and carries no |
| 171 | + /// further information about what went wrong. |
| 172 | + /// |
| 173 | + /// **Known problems:** Of course, this lint assumes that `Result` is used |
| 174 | + /// for a fallible operation (which is after all the intended use). However |
| 175 | + /// code may opt to (mis)use it as a basic two-variant-enum. In that case, |
| 176 | + /// the suggestion is misguided, and the code should use a custom enum |
| 177 | + /// instead. |
| 178 | + /// |
| 179 | + /// **Examples:** |
| 180 | + /// ```rust |
| 181 | + /// pub fn read_u8() -> Result<u8, ()> { Err(()) } |
| 182 | + /// ``` |
| 183 | + /// should become |
| 184 | + /// ```rust,should_panic |
| 185 | + /// use std::fmt; |
| 186 | + /// |
| 187 | + /// #[derive(Debug)] |
| 188 | + /// pub struct EndOfStream; |
| 189 | + /// |
| 190 | + /// impl fmt::Display for EndOfStream { |
| 191 | + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 192 | + /// write!(f, "End of Stream") |
| 193 | + /// } |
| 194 | + /// } |
| 195 | + /// |
| 196 | + /// impl std::error::Error for EndOfStream { } |
| 197 | + /// |
| 198 | + /// pub fn read_u8() -> Result<u8, EndOfStream> { Err(EndOfStream) } |
| 199 | + ///# fn main() { |
| 200 | + ///# read_u8().unwrap(); |
| 201 | + ///# } |
| 202 | + /// ``` |
| 203 | + /// |
| 204 | + /// Note that there are crates that simplify creating the error type, e.g. |
| 205 | + /// [`thiserror`](https://docs.rs/thiserror). |
| 206 | + pub RESULT_UNIT_ERR, |
| 207 | + style, |
| 208 | + "public function returning `Result` with an `Err` type of `()`" |
| 209 | +} |
| 210 | + |
| 211 | +#[derive(Copy, Clone)] |
| 212 | +pub struct Functions { |
| 213 | + too_many_arguments_threshold: u64, |
| 214 | + too_many_lines_threshold: u64, |
| 215 | +} |
| 216 | + |
| 217 | +impl Functions { |
| 218 | + pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64) -> Self { |
| 219 | + Self { |
| 220 | + too_many_arguments_threshold, |
| 221 | + too_many_lines_threshold, |
| 222 | + } |
| 223 | + } |
| 224 | +} |
| 225 | + |
| 226 | +impl_lint_pass!(Functions => [ |
| 227 | + TOO_MANY_ARGUMENTS, |
| 228 | + TOO_MANY_LINES, |
| 229 | + NOT_UNSAFE_PTR_ARG_DEREF, |
| 230 | + MUST_USE_UNIT, |
| 231 | + DOUBLE_MUST_USE, |
| 232 | + MUST_USE_CANDIDATE, |
| 233 | + RESULT_UNIT_ERR, |
| 234 | +]); |
| 235 | + |
| 236 | +impl<'tcx> LateLintPass<'tcx> for Functions { |
| 237 | + fn check_fn( |
| 238 | + &mut self, |
| 239 | + cx: &LateContext<'tcx>, |
| 240 | + kind: intravisit::FnKind<'tcx>, |
| 241 | + decl: &'tcx hir::FnDecl<'_>, |
| 242 | + body: &'tcx hir::Body<'_>, |
| 243 | + span: Span, |
| 244 | + hir_id: hir::HirId, |
| 245 | + ) { |
| 246 | + too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); |
| 247 | + too_many_lines::check_fn(cx, span, body, self.too_many_lines_threshold); |
| 248 | + not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id); |
| 249 | + } |
| 250 | + |
| 251 | + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { |
| 252 | + must_use::check_item(cx, item); |
| 253 | + result_unit_err::check_item(cx, item); |
| 254 | + } |
| 255 | + |
| 256 | + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { |
| 257 | + must_use::check_impl_item(cx, item); |
| 258 | + result_unit_err::check_impl_item(cx, item); |
| 259 | + } |
| 260 | + |
| 261 | + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { |
| 262 | + too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold); |
| 263 | + not_unsafe_ptr_arg_deref::check_trait_item(cx, item); |
| 264 | + must_use::check_trait_item(cx, item); |
| 265 | + result_unit_err::check_trait_item(cx, item); |
| 266 | + } |
| 267 | +} |
0 commit comments