]>
Commit | Line | Data |
---|---|---|
9c376795 FG |
1 | use crate::lints::{ |
2 | PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag, | |
9ffffee4 FG |
3 | UnusedAllocationMutDiag, UnusedClosure, UnusedDef, UnusedDefSuggestion, UnusedDelim, |
4 | UnusedDelimSuggestion, UnusedGenerator, UnusedImportBracesDiag, UnusedOp, UnusedResult, | |
9c376795 | 5 | }; |
ba9703b0 | 6 | use crate::Lint; |
dfeec247 | 7 | use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; |
3dfed10e | 8 | use rustc_ast as ast; |
94222f64 | 9 | use rustc_ast::util::{classify, parser}; |
3dfed10e | 10 | use rustc_ast::{ExprKind, StmtKind}; |
9c376795 | 11 | use rustc_errors::{pluralize, MultiSpan}; |
dfeec247 XL |
12 | use rustc_hir as hir; |
13 | use rustc_hir::def::{DefKind, Res}; | |
14 | use rustc_hir::def_id::DefId; | |
353b0b11 | 15 | use rustc_infer::traits::util::elaborate; |
ba9703b0 | 16 | use rustc_middle::ty::adjustment; |
353b0b11 | 17 | use rustc_middle::ty::{self, Ty}; |
dfeec247 XL |
18 | use rustc_span::symbol::Symbol; |
19 | use rustc_span::symbol::{kw, sym}; | |
f2b60f7d | 20 | use rustc_span::{BytePos, Span}; |
487cf647 | 21 | use std::iter; |
b039eaaf | 22 | |
b039eaaf | 23 | declare_lint! { |
1b1a35ee XL |
24 | /// The `unused_must_use` lint detects unused result of a type flagged as |
25 | /// `#[must_use]`. | |
26 | /// | |
27 | /// ### Example | |
28 | /// | |
29 | /// ```rust | |
30 | /// fn returns_result() -> Result<(), ()> { | |
31 | /// Ok(()) | |
32 | /// } | |
33 | /// | |
34 | /// fn main() { | |
35 | /// returns_result(); | |
36 | /// } | |
37 | /// ``` | |
38 | /// | |
39 | /// {{produces}} | |
40 | /// | |
41 | /// ### Explanation | |
42 | /// | |
43 | /// The `#[must_use]` attribute is an indicator that it is a mistake to | |
44 | /// ignore the value. See [the reference] for more details. | |
45 | /// | |
46 | /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute | |
b039eaaf SL |
47 | pub UNUSED_MUST_USE, |
48 | Warn, | |
416331ca | 49 | "unused result of a type flagged as `#[must_use]`", |
e74abb32 | 50 | report_in_external_macro |
b039eaaf SL |
51 | } |
52 | ||
53 | declare_lint! { | |
1b1a35ee XL |
54 | /// The `unused_results` lint checks for the unused result of an |
55 | /// expression in a statement. | |
56 | /// | |
57 | /// ### Example | |
58 | /// | |
59 | /// ```rust,compile_fail | |
60 | /// #![deny(unused_results)] | |
61 | /// fn foo<T>() -> T { panic!() } | |
62 | /// | |
63 | /// fn main() { | |
64 | /// foo::<usize>(); | |
65 | /// } | |
66 | /// ``` | |
67 | /// | |
68 | /// {{produces}} | |
69 | /// | |
70 | /// ### Explanation | |
71 | /// | |
72 | /// Ignoring the return value of a function may indicate a mistake. In | |
73 | /// cases were it is almost certain that the result should be used, it is | |
74 | /// recommended to annotate the function with the [`must_use` attribute]. | |
75 | /// Failure to use such a return value will trigger the [`unused_must_use` | |
76 | /// lint] which is warn-by-default. The `unused_results` lint is | |
77 | /// essentially the same, but triggers for *all* return values. | |
78 | /// | |
79 | /// This lint is "allow" by default because it can be noisy, and may not be | |
80 | /// an actual problem. For example, calling the `remove` method of a `Vec` | |
81 | /// or `HashMap` returns the previous value, which you may not care about. | |
82 | /// Using this lint would require explicitly ignoring or discarding such | |
83 | /// values. | |
84 | /// | |
85 | /// [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute | |
86 | /// [`unused_must_use` lint]: warn-by-default.html#unused-must-use | |
b039eaaf SL |
87 | pub UNUSED_RESULTS, |
88 | Allow, | |
89 | "unused result of an expression in a statement" | |
90 | } | |
91 | ||
532ac7d7 | 92 | declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]); |
b039eaaf | 93 | |
f035d41b XL |
94 | impl<'tcx> LateLintPass<'tcx> for UnusedResults { |
95 | fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { | |
487cf647 | 96 | let hir::StmtKind::Semi(expr) = s.kind else { return; }; |
b039eaaf | 97 | |
e74abb32 | 98 | if let hir::ExprKind::Ret(..) = expr.kind { |
b039eaaf SL |
99 | return; |
100 | } | |
101 | ||
487cf647 FG |
102 | if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind |
103 | && let ty = cx.typeck_results().expr_ty(&await_expr) | |
9c376795 | 104 | && let ty::Alias(ty::Opaque, ty::AliasTy { def_id: future_def_id, .. }) = ty.kind() |
487cf647 | 105 | && cx.tcx.ty_is_opaque_future(ty) |
487cf647 | 106 | && let async_fn_def_id = cx.tcx.parent(*future_def_id) |
353b0b11 FG |
107 | && matches!(cx.tcx.def_kind(async_fn_def_id), DefKind::Fn | DefKind::AssocFn) |
108 | // Check that this `impl Future` actually comes from an `async fn` | |
109 | && cx.tcx.asyncness(async_fn_def_id).is_async() | |
487cf647 FG |
110 | && check_must_use_def( |
111 | cx, | |
112 | async_fn_def_id, | |
113 | expr.span, | |
114 | "output of future returned by ", | |
115 | "", | |
116 | ) | |
117 | { | |
118 | // We have a bare `foo().await;` on an opaque type from an async function that was | |
119 | // annotated with `#[must_use]`. | |
120 | return; | |
121 | } | |
122 | ||
3dfed10e | 123 | let ty = cx.typeck_results().expr_ty(&expr); |
3b2f2976 | 124 | |
487cf647 FG |
125 | let must_use_result = is_ty_must_use(cx, ty, &expr, expr.span); |
126 | let type_lint_emitted_or_suppressed = match must_use_result { | |
127 | Some(path) => { | |
353b0b11 | 128 | emit_must_use_untranslated(cx, &path, "", "", 1, false); |
487cf647 | 129 | true |
dfeec247 | 130 | } |
487cf647 | 131 | None => false, |
83c7162d | 132 | }; |
487cf647 FG |
133 | |
134 | let fn_warned = check_fn_must_use(cx, expr); | |
135 | ||
136 | if !fn_warned && type_lint_emitted_or_suppressed { | |
0bf4aa26 XL |
137 | // We don't warn about unused unit or uninhabited types. |
138 | // (See https://github.com/rust-lang/rust/issues/43806 for details.) | |
139 | return; | |
83c7162d | 140 | } |
0bf4aa26 | 141 | |
e74abb32 | 142 | let must_use_op = match expr.kind { |
83c7162d XL |
143 | // Hardcoding operators here seemed more expedient than the |
144 | // refactoring that would be needed to look up the `#[must_use]` | |
145 | // attribute which does exist on the comparison trait methods | |
dfeec247 XL |
146 | hir::ExprKind::Binary(bin_op, ..) => match bin_op.node { |
147 | hir::BinOpKind::Eq | |
148 | | hir::BinOpKind::Lt | |
149 | | hir::BinOpKind::Le | |
150 | | hir::BinOpKind::Ne | |
151 | | hir::BinOpKind::Ge | |
152 | | hir::BinOpKind::Gt => Some("comparison"), | |
153 | hir::BinOpKind::Add | |
154 | | hir::BinOpKind::Sub | |
155 | | hir::BinOpKind::Div | |
156 | | hir::BinOpKind::Mul | |
157 | | hir::BinOpKind::Rem => Some("arithmetic operation"), | |
158 | hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"), | |
159 | hir::BinOpKind::BitXor | |
160 | | hir::BinOpKind::BitAnd | |
161 | | hir::BinOpKind::BitOr | |
162 | | hir::BinOpKind::Shl | |
163 | | hir::BinOpKind::Shr => Some("bitwise operation"), | |
83c7162d | 164 | }, |
136023e0 | 165 | hir::ExprKind::AddrOf(..) => Some("borrow"), |
8faf50e0 | 166 | hir::ExprKind::Unary(..) => Some("unary operation"), |
dfeec247 | 167 | _ => None, |
83c7162d XL |
168 | }; |
169 | ||
487cf647 FG |
170 | let mut op_warned = false; |
171 | ||
83c7162d | 172 | if let Some(must_use_op) = must_use_op { |
9c376795 FG |
173 | cx.emit_spanned_lint( |
174 | UNUSED_MUST_USE, | |
175 | expr.span, | |
176 | UnusedOp { | |
177 | op: must_use_op, | |
178 | label: expr.span, | |
179 | suggestion: expr.span.shrink_to_lo(), | |
180 | }, | |
181 | ); | |
83c7162d | 182 | op_warned = true; |
3b2f2976 XL |
183 | } |
184 | ||
487cf647 | 185 | if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) { |
9c376795 | 186 | cx.emit_spanned_lint(UNUSED_RESULTS, s.span, UnusedResult { ty }); |
b039eaaf SL |
187 | } |
188 | ||
487cf647 FG |
189 | fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { |
190 | let maybe_def_id = match expr.kind { | |
191 | hir::ExprKind::Call(ref callee, _) => { | |
192 | match callee.kind { | |
193 | hir::ExprKind::Path(ref qpath) => { | |
194 | match cx.qpath_res(qpath, callee.hir_id) { | |
195 | Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id), | |
196 | // `Res::Local` if it was a closure, for which we | |
197 | // do not currently support must-use linting | |
198 | _ => None, | |
199 | } | |
200 | } | |
201 | _ => None, | |
202 | } | |
203 | } | |
204 | hir::ExprKind::MethodCall(..) => { | |
205 | cx.typeck_results().type_dependent_def_id(expr.hir_id) | |
206 | } | |
207 | _ => None, | |
208 | }; | |
209 | if let Some(def_id) = maybe_def_id { | |
210 | check_must_use_def(cx, def_id, expr.span, "return value of ", "") | |
211 | } else { | |
212 | false | |
213 | } | |
214 | } | |
215 | ||
216 | /// A path through a type to a must_use source. Contains useful info for the lint. | |
217 | #[derive(Debug)] | |
218 | enum MustUsePath { | |
219 | /// Suppress must_use checking. | |
220 | Suppressed, | |
221 | /// The root of the normal must_use lint with an optional message. | |
222 | Def(Span, DefId, Option<Symbol>), | |
223 | Boxed(Box<Self>), | |
224 | Opaque(Box<Self>), | |
225 | TraitObject(Box<Self>), | |
226 | TupleElement(Vec<(usize, Self)>), | |
227 | Array(Box<Self>, u64), | |
228 | /// The root of the unused_closures lint. | |
229 | Closure(Span), | |
230 | /// The root of the unused_generators lint. | |
231 | Generator(Span), | |
232 | } | |
233 | ||
234 | #[instrument(skip(cx, expr), level = "debug", ret)] | |
235 | fn is_ty_must_use<'tcx>( | |
f035d41b | 236 | cx: &LateContext<'tcx>, |
dc9dc135 | 237 | ty: Ty<'tcx>, |
dfeec247 | 238 | expr: &hir::Expr<'_>, |
dc9dc135 | 239 | span: Span, |
487cf647 | 240 | ) -> Option<MustUsePath> { |
ba9703b0 | 241 | if ty.is_unit() |
487cf647 FG |
242 | || !ty.is_inhabited_from( |
243 | cx.tcx, | |
ba9703b0 | 244 | cx.tcx.parent_module(expr.hir_id).to_def_id(), |
ba9703b0 XL |
245 | cx.param_env, |
246 | ) | |
dc9dc135 | 247 | { |
487cf647 | 248 | return Some(MustUsePath::Suppressed); |
dc9dc135 XL |
249 | } |
250 | ||
1b1a35ee | 251 | match *ty.kind() { |
dc9dc135 XL |
252 | ty::Adt(..) if ty.is_box() => { |
253 | let boxed_ty = ty.boxed_ty(); | |
487cf647 FG |
254 | is_ty_must_use(cx, boxed_ty, expr, span) |
255 | .map(|inner| MustUsePath::Boxed(Box::new(inner))) | |
dc9dc135 | 256 | } |
487cf647 | 257 | ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), |
9c376795 | 258 | ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => { |
49aad941 | 259 | elaborate(cx.tcx, cx.tcx.explicit_item_bounds(def).subst_identity_iter_copied()) |
353b0b11 FG |
260 | // We only care about self bounds for the impl-trait |
261 | .filter_only_self() | |
262 | .find_map(|(pred, _span)| { | |
263 | // We only look at the `DefId`, so it is safe to skip the binder here. | |
264 | if let ty::PredicateKind::Clause(ty::Clause::Trait( | |
265 | ref poly_trait_predicate, | |
266 | )) = pred.kind().skip_binder() | |
267 | { | |
268 | let def_id = poly_trait_predicate.trait_ref.def_id; | |
269 | ||
270 | is_def_must_use(cx, def_id, span) | |
271 | } else { | |
272 | None | |
273 | } | |
274 | }) | |
275 | .map(|inner| MustUsePath::Opaque(Box::new(inner))) | |
dc9dc135 | 276 | } |
9c376795 FG |
277 | ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| { |
278 | if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() | |
279 | { | |
280 | let def_id = trait_ref.def_id; | |
281 | is_def_must_use(cx, def_id, span) | |
282 | .map(|inner| MustUsePath::TraitObject(Box::new(inner))) | |
283 | } else { | |
284 | None | |
285 | } | |
286 | }), | |
487cf647 FG |
287 | ty::Tuple(tys) => { |
288 | let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind { | |
289 | debug_assert_eq!(elem_exprs.len(), tys.len()); | |
290 | elem_exprs | |
dc9dc135 | 291 | } else { |
5e7ed085 | 292 | &[] |
dc9dc135 | 293 | }; |
487cf647 FG |
294 | |
295 | // Default to `expr`. | |
296 | let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr)); | |
297 | ||
298 | let nested_must_use = tys | |
299 | .iter() | |
300 | .zip(elem_exprs) | |
301 | .enumerate() | |
302 | .filter_map(|(i, (ty, expr))| { | |
303 | is_ty_must_use(cx, ty, expr, expr.span).map(|path| (i, path)) | |
304 | }) | |
305 | .collect::<Vec<_>>(); | |
306 | ||
307 | if !nested_must_use.is_empty() { | |
308 | Some(MustUsePath::TupleElement(nested_must_use)) | |
309 | } else { | |
310 | None | |
dc9dc135 | 311 | } |
dc9dc135 | 312 | } |
9ffffee4 | 313 | ty::Array(ty, len) => match len.try_eval_target_usize(cx.tcx, cx.param_env) { |
29967ef6 | 314 | // If the array is empty we don't lint, to avoid false positives |
487cf647 | 315 | Some(0) | None => None, |
dc9dc135 | 316 | // If the array is definitely non-empty, we can do `#[must_use]` checking. |
487cf647 FG |
317 | Some(len) => is_ty_must_use(cx, ty, expr, span) |
318 | .map(|inner| MustUsePath::Array(Box::new(inner), len)), | |
dfeec247 | 319 | }, |
487cf647 FG |
320 | ty::Closure(..) => Some(MustUsePath::Closure(span)), |
321 | ty::Generator(def_id, ..) => { | |
322 | // async fn should be treated as "implementor of `Future`" | |
323 | let must_use = if cx.tcx.generator_is_async(def_id) { | |
324 | let def_id = cx.tcx.lang_items().future_trait().unwrap(); | |
325 | is_def_must_use(cx, def_id, span) | |
326 | .map(|inner| MustUsePath::Opaque(Box::new(inner))) | |
327 | } else { | |
328 | None | |
329 | }; | |
330 | must_use.or(Some(MustUsePath::Generator(span))) | |
331 | } | |
332 | _ => None, | |
333 | } | |
334 | } | |
335 | ||
336 | fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option<MustUsePath> { | |
337 | if let Some(attr) = cx.tcx.get_attr(def_id, sym::must_use) { | |
338 | // check for #[must_use = "..."] | |
339 | let reason = attr.value_str(); | |
340 | Some(MustUsePath::Def(span, def_id, reason)) | |
341 | } else { | |
342 | None | |
343 | } | |
344 | } | |
345 | ||
346 | // Returns whether further errors should be suppressed because either a lint has been emitted or the type should be ignored. | |
347 | fn check_must_use_def( | |
348 | cx: &LateContext<'_>, | |
349 | def_id: DefId, | |
350 | span: Span, | |
351 | descr_pre_path: &str, | |
352 | descr_post_path: &str, | |
353 | ) -> bool { | |
354 | is_def_must_use(cx, def_id, span) | |
355 | .map(|must_use_path| { | |
356 | emit_must_use_untranslated( | |
357 | cx, | |
358 | &must_use_path, | |
359 | descr_pre_path, | |
360 | descr_post_path, | |
361 | 1, | |
353b0b11 | 362 | false, |
487cf647 FG |
363 | ) |
364 | }) | |
365 | .is_some() | |
366 | } | |
367 | ||
368 | #[instrument(skip(cx), level = "debug")] | |
369 | fn emit_must_use_untranslated( | |
370 | cx: &LateContext<'_>, | |
371 | path: &MustUsePath, | |
372 | descr_pre: &str, | |
373 | descr_post: &str, | |
374 | plural_len: usize, | |
353b0b11 | 375 | is_inner: bool, |
487cf647 FG |
376 | ) { |
377 | let plural_suffix = pluralize!(plural_len); | |
378 | ||
379 | match path { | |
380 | MustUsePath::Suppressed => {} | |
381 | MustUsePath::Boxed(path) => { | |
382 | let descr_pre = &format!("{}boxed ", descr_pre); | |
353b0b11 | 383 | emit_must_use_untranslated(cx, path, descr_pre, descr_post, plural_len, true); |
487cf647 FG |
384 | } |
385 | MustUsePath::Opaque(path) => { | |
386 | let descr_pre = &format!("{}implementer{} of ", descr_pre, plural_suffix); | |
353b0b11 | 387 | emit_must_use_untranslated(cx, path, descr_pre, descr_post, plural_len, true); |
487cf647 FG |
388 | } |
389 | MustUsePath::TraitObject(path) => { | |
390 | let descr_post = &format!(" trait object{}{}", plural_suffix, descr_post); | |
353b0b11 | 391 | emit_must_use_untranslated(cx, path, descr_pre, descr_post, plural_len, true); |
487cf647 FG |
392 | } |
393 | MustUsePath::TupleElement(elems) => { | |
394 | for (index, path) in elems { | |
395 | let descr_post = &format!(" in tuple element {}", index); | |
353b0b11 FG |
396 | emit_must_use_untranslated( |
397 | cx, path, descr_pre, descr_post, plural_len, true, | |
398 | ); | |
487cf647 FG |
399 | } |
400 | } | |
401 | MustUsePath::Array(path, len) => { | |
402 | let descr_pre = &format!("{}array{} of ", descr_pre, plural_suffix); | |
403 | emit_must_use_untranslated( | |
404 | cx, | |
405 | path, | |
406 | descr_pre, | |
407 | descr_post, | |
408 | plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)), | |
353b0b11 | 409 | true, |
487cf647 FG |
410 | ); |
411 | } | |
412 | MustUsePath::Closure(span) => { | |
9c376795 | 413 | cx.emit_spanned_lint( |
2b03887a | 414 | UNUSED_MUST_USE, |
487cf647 | 415 | *span, |
9c376795 | 416 | UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post }, |
2b03887a | 417 | ); |
3dfed10e | 418 | } |
487cf647 | 419 | MustUsePath::Generator(span) => { |
9c376795 | 420 | cx.emit_spanned_lint( |
2b03887a | 421 | UNUSED_MUST_USE, |
487cf647 | 422 | *span, |
9c376795 | 423 | UnusedGenerator { count: plural_len, pre: descr_pre, post: descr_post }, |
2b03887a | 424 | ); |
3dfed10e | 425 | } |
487cf647 | 426 | MustUsePath::Def(span, def_id, reason) => { |
9c376795 FG |
427 | cx.emit_spanned_lint( |
428 | UNUSED_MUST_USE, | |
429 | *span, | |
430 | UnusedDef { | |
431 | pre: descr_pre, | |
432 | post: descr_post, | |
433 | cx, | |
434 | def_id: *def_id, | |
435 | note: *reason, | |
353b0b11 FG |
436 | suggestion: (!is_inner) |
437 | .then_some(UnusedDefSuggestion { span: span.shrink_to_lo() }), | |
9c376795 FG |
438 | }, |
439 | ); | |
487cf647 | 440 | } |
b039eaaf | 441 | } |
b039eaaf SL |
442 | } |
443 | } | |
444 | } | |
445 | ||
b039eaaf | 446 | declare_lint! { |
1b1a35ee XL |
447 | /// The `path_statements` lint detects path statements with no effect. |
448 | /// | |
449 | /// ### Example | |
450 | /// | |
451 | /// ```rust | |
452 | /// let x = 42; | |
453 | /// | |
454 | /// x; | |
455 | /// ``` | |
456 | /// | |
457 | /// {{produces}} | |
458 | /// | |
459 | /// ### Explanation | |
460 | /// | |
461 | /// It is usually a mistake to have a statement that has no effect. | |
b039eaaf SL |
462 | pub PATH_STATEMENTS, |
463 | Warn, | |
464 | "path statements with no effect" | |
465 | } | |
466 | ||
532ac7d7 | 467 | declare_lint_pass!(PathStatements => [PATH_STATEMENTS]); |
b039eaaf | 468 | |
f035d41b XL |
469 | impl<'tcx> LateLintPass<'tcx> for PathStatements { |
470 | fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { | |
3dfed10e | 471 | if let hir::StmtKind::Semi(expr) = s.kind { |
e74abb32 | 472 | if let hir::ExprKind::Path(_) = expr.kind { |
2b03887a FG |
473 | let ty = cx.typeck_results().expr_ty(expr); |
474 | if ty.needs_drop(cx.tcx, cx.param_env) { | |
9c376795 FG |
475 | let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) |
476 | { | |
477 | PathStatementDropSub::Suggestion { span: s.span, snippet } | |
478 | } else { | |
479 | PathStatementDropSub::Help { span: s.span } | |
480 | }; | |
481 | cx.emit_spanned_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub }) | |
2b03887a | 482 | } else { |
9c376795 | 483 | cx.emit_spanned_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect); |
2b03887a | 484 | } |
b039eaaf | 485 | } |
b039eaaf SL |
486 | } |
487 | } | |
488 | } | |
489 | ||
ba9703b0 XL |
490 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
491 | enum UnusedDelimsCtx { | |
492 | FunctionArg, | |
493 | MethodArg, | |
494 | AssignedValue, | |
94222f64 | 495 | AssignedValueLetElse, |
ba9703b0 XL |
496 | IfCond, |
497 | WhileCond, | |
498 | ForIterExpr, | |
499 | MatchScrutineeExpr, | |
500 | ReturnValue, | |
501 | BlockRetValue, | |
502 | LetScrutineeExpr, | |
503 | ArrayLenExpr, | |
504 | AnonConst, | |
064997fb | 505 | MatchArmExpr, |
9ffffee4 | 506 | IndexExpr, |
b039eaaf SL |
507 | } |
508 | ||
ba9703b0 XL |
509 | impl From<UnusedDelimsCtx> for &'static str { |
510 | fn from(ctx: UnusedDelimsCtx) -> &'static str { | |
511 | match ctx { | |
512 | UnusedDelimsCtx::FunctionArg => "function argument", | |
513 | UnusedDelimsCtx::MethodArg => "method argument", | |
94222f64 XL |
514 | UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => { |
515 | "assigned value" | |
516 | } | |
ba9703b0 XL |
517 | UnusedDelimsCtx::IfCond => "`if` condition", |
518 | UnusedDelimsCtx::WhileCond => "`while` condition", | |
519 | UnusedDelimsCtx::ForIterExpr => "`for` iterator expression", | |
520 | UnusedDelimsCtx::MatchScrutineeExpr => "`match` scrutinee expression", | |
521 | UnusedDelimsCtx::ReturnValue => "`return` value", | |
522 | UnusedDelimsCtx::BlockRetValue => "block return value", | |
523 | UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression", | |
524 | UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression", | |
064997fb | 525 | UnusedDelimsCtx::MatchArmExpr => "match arm expression", |
9ffffee4 | 526 | UnusedDelimsCtx::IndexExpr => "index expression", |
ba9703b0 | 527 | } |
416331ca | 528 | } |
ba9703b0 | 529 | } |
416331ca | 530 | |
ba9703b0 XL |
531 | /// Used by both `UnusedParens` and `UnusedBraces` to prevent code duplication. |
532 | trait UnusedDelimLint { | |
533 | const DELIM_STR: &'static str; | |
534 | ||
535 | /// Due to `ref` pattern, there can be a difference between using | |
536 | /// `{ expr }` and `expr` in pattern-matching contexts. This means | |
537 | /// that we should only lint `unused_parens` and not `unused_braces` | |
538 | /// in this case. | |
539 | /// | |
540 | /// ```rust | |
541 | /// let mut a = 7; | |
542 | /// let ref b = { a }; // We actually borrow a copy of `a` here. | |
543 | /// a += 1; // By mutating `a` we invalidate any borrows of `a`. | |
544 | /// assert_eq!(b + 1, a); // `b` does not borrow `a`, so we can still use it here. | |
545 | /// ``` | |
546 | const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool; | |
547 | ||
548 | // this cannot be a constant is it refers to a static. | |
549 | fn lint(&self) -> &'static Lint; | |
550 | ||
551 | fn check_unused_delims_expr( | |
dfeec247 XL |
552 | &self, |
553 | cx: &EarlyContext<'_>, | |
554 | value: &ast::Expr, | |
ba9703b0 | 555 | ctx: UnusedDelimsCtx, |
dfeec247 XL |
556 | followed_by_block: bool, |
557 | left_pos: Option<BytePos>, | |
558 | right_pos: Option<BytePos>, | |
ba9703b0 XL |
559 | ); |
560 | ||
94222f64 XL |
561 | fn is_expr_delims_necessary( |
562 | inner: &ast::Expr, | |
563 | followed_by_block: bool, | |
564 | followed_by_else: bool, | |
565 | ) -> bool { | |
566 | if followed_by_else { | |
567 | match inner.kind { | |
568 | ast::ExprKind::Binary(op, ..) if op.node.lazy() => return true, | |
569 | _ if classify::expr_trailing_brace(inner).is_some() => return true, | |
570 | _ => {} | |
571 | } | |
572 | } | |
573 | ||
49aad941 FG |
574 | // Check if LHS needs parens to prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`. |
575 | { | |
f9f354fc XL |
576 | let mut innermost = inner; |
577 | loop { | |
c295e0f8 | 578 | innermost = match &innermost.kind { |
49aad941 | 579 | ExprKind::Binary(_op, lhs, _rhs) => lhs, |
c295e0f8 XL |
580 | ExprKind::Call(fn_, _params) => fn_, |
581 | ExprKind::Cast(expr, _ty) => expr, | |
582 | ExprKind::Type(expr, _ty) => expr, | |
583 | ExprKind::Index(base, _subscript) => base, | |
49aad941 | 584 | _ => break, |
c295e0f8 XL |
585 | }; |
586 | if !classify::expr_requires_semi_to_be_stmt(innermost) { | |
49aad941 | 587 | return true; |
f9f354fc | 588 | } |
b039eaaf | 589 | } |
49aad941 | 590 | } |
f9f354fc | 591 | |
49aad941 FG |
592 | // Check if RHS needs parens to prevent false-positives in cases like `if (() == return) {}`. |
593 | if !followed_by_block { | |
594 | return false; | |
595 | } | |
596 | let mut innermost = inner; | |
597 | loop { | |
598 | innermost = match &innermost.kind { | |
599 | ExprKind::Unary(_op, expr) => expr, | |
600 | ExprKind::Binary(_op, _lhs, rhs) => rhs, | |
601 | ExprKind::AssignOp(_op, _lhs, rhs) => rhs, | |
602 | ExprKind::Assign(_lhs, rhs, _span) => rhs, | |
603 | ||
604 | ExprKind::Ret(_) | ExprKind::Yield(..) | ExprKind::Yeet(..) => return true, | |
605 | ||
606 | ExprKind::Break(_label, None) => return false, | |
607 | ExprKind::Break(_label, Some(break_expr)) => { | |
608 | return matches!(break_expr.kind, ExprKind::Block(..)); | |
609 | } | |
610 | ||
611 | ExprKind::Range(_lhs, Some(rhs), _limits) => { | |
612 | return matches!(rhs.kind, ExprKind::Block(..)); | |
613 | } | |
614 | ||
615 | _ => return parser::contains_exterior_struct_lit(&inner), | |
616 | } | |
617 | } | |
b039eaaf | 618 | } |
0bf4aa26 | 619 | |
ba9703b0 | 620 | fn emit_unused_delims_expr( |
e1599b0c XL |
621 | &self, |
622 | cx: &EarlyContext<'_>, | |
ba9703b0 XL |
623 | value: &ast::Expr, |
624 | ctx: UnusedDelimsCtx, | |
625 | left_pos: Option<BytePos>, | |
626 | right_pos: Option<BytePos>, | |
e1599b0c | 627 | ) { |
487cf647 FG |
628 | // If `value` has `ExprKind::Err`, unused delim lint can be broken. |
629 | // For example, the following code caused ICE. | |
630 | // This is because the `ExprKind::Call` in `value` has `ExprKind::Err` as its argument | |
631 | // and this leads to wrong spans. #104897 | |
632 | // | |
633 | // ``` | |
634 | // fn f(){(print!(á | |
635 | // ``` | |
636 | use rustc_ast::visit::{walk_expr, Visitor}; | |
637 | struct ErrExprVisitor { | |
638 | has_error: bool, | |
639 | } | |
640 | impl<'ast> Visitor<'ast> for ErrExprVisitor { | |
641 | fn visit_expr(&mut self, expr: &'ast ast::Expr) { | |
642 | if let ExprKind::Err = expr.kind { | |
643 | self.has_error = true; | |
644 | return; | |
645 | } | |
646 | walk_expr(self, expr) | |
647 | } | |
648 | } | |
649 | let mut visitor = ErrExprVisitor { has_error: false }; | |
650 | visitor.visit_expr(value); | |
651 | if visitor.has_error { | |
652 | return; | |
653 | } | |
c295e0f8 | 654 | let spans = match value.kind { |
49aad941 FG |
655 | ast::ExprKind::Block(ref block, None) if block.stmts.len() == 1 => block.stmts[0] |
656 | .span | |
657 | .find_ancestor_inside(value.span) | |
658 | .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi()))), | |
c295e0f8 | 659 | ast::ExprKind::Paren(ref expr) => { |
49aad941 FG |
660 | expr.span.find_ancestor_inside(value.span).map(|expr_span| { |
661 | (value.span.with_hi(expr_span.lo()), value.span.with_lo(expr_span.hi())) | |
662 | }) | |
c295e0f8 XL |
663 | } |
664 | _ => return, | |
ba9703b0 XL |
665 | }; |
666 | let keep_space = ( | |
49aad941 FG |
667 | left_pos.is_some_and(|s| s >= value.span.lo()), |
668 | right_pos.is_some_and(|s| s <= value.span.hi()), | |
ba9703b0 | 669 | ); |
f2b60f7d | 670 | self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space); |
0bf4aa26 XL |
671 | } |
672 | ||
ba9703b0 XL |
673 | fn emit_unused_delims( |
674 | &self, | |
dfeec247 | 675 | cx: &EarlyContext<'_>, |
f2b60f7d FG |
676 | value_span: Span, |
677 | spans: Option<(Span, Span)>, | |
dfeec247 XL |
678 | msg: &str, |
679 | keep_space: (bool, bool), | |
680 | ) { | |
f2b60f7d | 681 | let primary_span = if let Some((lo, hi)) = spans { |
9ffffee4 FG |
682 | if hi.is_empty() { |
683 | // do not point at delims that do not exist | |
684 | return; | |
685 | } | |
f2b60f7d FG |
686 | MultiSpan::from(vec![lo, hi]) |
687 | } else { | |
688 | MultiSpan::from(value_span) | |
689 | }; | |
9c376795 FG |
690 | let suggestion = spans.map(|(lo, hi)| { |
691 | let sm = cx.sess().source_map(); | |
692 | let lo_replace = | |
487cf647 FG |
693 | if keep_space.0 && |
694 | let Ok(snip) = sm.span_to_prev_source(lo) && !snip.ends_with(' ') { | |
9c376795 | 695 | " " |
487cf647 | 696 | } else { |
9c376795 | 697 | "" |
487cf647 FG |
698 | }; |
699 | ||
9c376795 | 700 | let hi_replace = |
487cf647 FG |
701 | if keep_space.1 && |
702 | let Ok(snip) = sm.span_to_next_source(hi) && !snip.starts_with(' ') { | |
9c376795 | 703 | " " |
487cf647 | 704 | } else { |
9c376795 | 705 | "" |
487cf647 | 706 | }; |
9c376795 FG |
707 | UnusedDelimSuggestion { |
708 | start_span: lo, | |
709 | start_replace: lo_replace, | |
710 | end_span: hi, | |
711 | end_replace: hi_replace, | |
f2b60f7d | 712 | } |
74b04a01 | 713 | }); |
9c376795 FG |
714 | cx.emit_spanned_lint( |
715 | self.lint(), | |
716 | primary_span, | |
717 | UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion }, | |
718 | ); | |
0bf4aa26 | 719 | } |
b039eaaf | 720 | |
9fa01778 | 721 | fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { |
3dfed10e | 722 | use rustc_ast::ExprKind::*; |
ba9703b0 XL |
723 | let (value, ctx, followed_by_block, left_pos, right_pos) = match e.kind { |
724 | // Do not lint `unused_braces` in `if let` expressions. | |
6a06907d | 725 | If(ref cond, ref block, _) |
94222f64 XL |
726 | if !matches!(cond.kind, Let(_, _, _)) |
727 | || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => | |
ba9703b0 | 728 | { |
dfeec247 | 729 | let left = e.span.lo() + rustc_span::BytePos(2); |
416331ca | 730 | let right = block.span.lo(); |
ba9703b0 | 731 | (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right)) |
416331ca XL |
732 | } |
733 | ||
3dfed10e XL |
734 | // Do not lint `unused_braces` in `while let` expressions. |
735 | While(ref cond, ref block, ..) | |
94222f64 XL |
736 | if !matches!(cond.kind, Let(_, _, _)) |
737 | || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => | |
3dfed10e | 738 | { |
dfeec247 | 739 | let left = e.span.lo() + rustc_span::BytePos(5); |
416331ca | 740 | let right = block.span.lo(); |
ba9703b0 | 741 | (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right)) |
dfeec247 | 742 | } |
416331ca | 743 | |
ba9703b0 XL |
744 | ForLoop(_, ref cond, ref block, ..) => { |
745 | (cond, UnusedDelimsCtx::ForIterExpr, true, None, Some(block.span.lo())) | |
416331ca XL |
746 | } |
747 | ||
ba9703b0 | 748 | Match(ref head, _) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { |
dfeec247 | 749 | let left = e.span.lo() + rustc_span::BytePos(5); |
ba9703b0 | 750 | (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None) |
416331ca XL |
751 | } |
752 | ||
753 | Ret(Some(ref value)) => { | |
dfeec247 | 754 | let left = e.span.lo() + rustc_span::BytePos(3); |
ba9703b0 | 755 | (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None) |
416331ca XL |
756 | } |
757 | ||
9ffffee4 FG |
758 | Index(_, ref value) => (value, UnusedDelimsCtx::IndexExpr, false, None, None), |
759 | ||
ba9703b0 XL |
760 | Assign(_, ref value, _) | AssignOp(.., ref value) => { |
761 | (value, UnusedDelimsCtx::AssignedValue, false, None, None) | |
762 | } | |
2c00a5a8 XL |
763 | // either function/method call, or something this lint doesn't care about |
764 | ref call_or_other => { | |
ba9703b0 XL |
765 | let (args_to_check, ctx) = match *call_or_other { |
766 | Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg), | |
487cf647 | 767 | MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg), |
2c00a5a8 | 768 | // actual catch-all arm |
0bf4aa26 XL |
769 | _ => { |
770 | return; | |
771 | } | |
772 | }; | |
2c00a5a8 XL |
773 | // Don't lint if this is a nested macro expansion: otherwise, the lint could |
774 | // trigger in situations that macro authors shouldn't have to care about, e.g., | |
775 | // when a parenthesized token tree matched in one macro expansion is matched as | |
776 | // an expression in another and used as a fn/method argument (Issue #47775) | |
e1599b0c XL |
777 | if e.span.ctxt().outer_expn_data().call_site.from_expansion() { |
778 | return; | |
2c00a5a8 | 779 | } |
2c00a5a8 | 780 | for arg in args_to_check { |
ba9703b0 | 781 | self.check_unused_delims_expr(cx, arg, ctx, false, None, None); |
2c00a5a8 XL |
782 | } |
783 | return; | |
784 | } | |
b039eaaf | 785 | }; |
ba9703b0 XL |
786 | self.check_unused_delims_expr(cx, &value, ctx, followed_by_block, left_pos, right_pos); |
787 | } | |
788 | ||
789 | fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { | |
790 | match s.kind { | |
791 | StmtKind::Local(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { | |
94222f64 XL |
792 | if let Some((init, els)) = local.kind.init_else_opt() { |
793 | let ctx = match els { | |
794 | None => UnusedDelimsCtx::AssignedValue, | |
795 | Some(_) => UnusedDelimsCtx::AssignedValueLetElse, | |
796 | }; | |
797 | self.check_unused_delims_expr(cx, init, ctx, false, None, None); | |
ba9703b0 XL |
798 | } |
799 | } | |
800 | StmtKind::Expr(ref expr) => { | |
801 | self.check_unused_delims_expr( | |
802 | cx, | |
803 | &expr, | |
804 | UnusedDelimsCtx::BlockRetValue, | |
805 | false, | |
806 | None, | |
807 | None, | |
808 | ); | |
809 | } | |
810 | _ => {} | |
811 | } | |
812 | } | |
813 | ||
814 | fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { | |
815 | use ast::ItemKind::*; | |
816 | ||
353b0b11 FG |
817 | if let Const(box ast::ConstItem { expr: Some(expr), .. }) |
818 | | Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind | |
819 | { | |
ba9703b0 XL |
820 | self.check_unused_delims_expr( |
821 | cx, | |
822 | expr, | |
823 | UnusedDelimsCtx::AssignedValue, | |
824 | false, | |
825 | None, | |
826 | None, | |
827 | ); | |
828 | } | |
829 | } | |
830 | } | |
831 | ||
832 | declare_lint! { | |
1b1a35ee XL |
833 | /// The `unused_parens` lint detects `if`, `match`, `while` and `return` |
834 | /// with parentheses; they do not need them. | |
835 | /// | |
836 | /// ### Examples | |
837 | /// | |
838 | /// ```rust | |
839 | /// if(true) {} | |
840 | /// ``` | |
841 | /// | |
842 | /// {{produces}} | |
843 | /// | |
844 | /// ### Explanation | |
845 | /// | |
3c0e092e | 846 | /// The parentheses are not needed, and should be removed. This is the |
1b1a35ee | 847 | /// preferred style for writing these expressions. |
ba9703b0 XL |
848 | pub(super) UNUSED_PARENS, |
849 | Warn, | |
850 | "`if`, `match`, `while` and `return` do not need parentheses" | |
851 | } | |
852 | ||
9c376795 FG |
853 | pub struct UnusedParens { |
854 | with_self_ty_parens: bool, | |
855 | } | |
856 | ||
857 | impl UnusedParens { | |
858 | pub fn new() -> Self { | |
859 | Self { with_self_ty_parens: false } | |
860 | } | |
861 | } | |
862 | ||
863 | impl_lint_pass!(UnusedParens => [UNUSED_PARENS]); | |
ba9703b0 XL |
864 | |
865 | impl UnusedDelimLint for UnusedParens { | |
866 | const DELIM_STR: &'static str = "parentheses"; | |
867 | ||
868 | const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = true; | |
869 | ||
870 | fn lint(&self) -> &'static Lint { | |
871 | UNUSED_PARENS | |
872 | } | |
873 | ||
874 | fn check_unused_delims_expr( | |
875 | &self, | |
876 | cx: &EarlyContext<'_>, | |
877 | value: &ast::Expr, | |
878 | ctx: UnusedDelimsCtx, | |
879 | followed_by_block: bool, | |
880 | left_pos: Option<BytePos>, | |
881 | right_pos: Option<BytePos>, | |
882 | ) { | |
883 | match value.kind { | |
884 | ast::ExprKind::Paren(ref inner) => { | |
94222f64 XL |
885 | let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse; |
886 | if !Self::is_expr_delims_necessary(inner, followed_by_block, followed_by_else) | |
ba9703b0 XL |
887 | && value.attrs.is_empty() |
888 | && !value.span.from_expansion() | |
29967ef6 XL |
889 | && (ctx != UnusedDelimsCtx::LetScrutineeExpr |
890 | || !matches!(inner.kind, ast::ExprKind::Binary( | |
891 | rustc_span::source_map::Spanned { node, .. }, | |
892 | _, | |
893 | _, | |
894 | ) if node.lazy())) | |
ba9703b0 XL |
895 | { |
896 | self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos) | |
897 | } | |
898 | } | |
94222f64 | 899 | ast::ExprKind::Let(_, ref expr, _) => { |
ba9703b0 XL |
900 | self.check_unused_delims_expr( |
901 | cx, | |
902 | expr, | |
903 | UnusedDelimsCtx::LetScrutineeExpr, | |
904 | followed_by_block, | |
905 | None, | |
906 | None, | |
907 | ); | |
908 | } | |
909 | _ => {} | |
910 | } | |
911 | } | |
912 | } | |
913 | ||
914 | impl UnusedParens { | |
915 | fn check_unused_parens_pat( | |
916 | &self, | |
917 | cx: &EarlyContext<'_>, | |
918 | value: &ast::Pat, | |
919 | avoid_or: bool, | |
920 | avoid_mut: bool, | |
487cf647 | 921 | keep_space: (bool, bool), |
ba9703b0 | 922 | ) { |
f2b60f7d | 923 | use ast::{BindingAnnotation, PatKind}; |
ba9703b0 XL |
924 | |
925 | if let PatKind::Paren(inner) = &value.kind { | |
926 | match inner.kind { | |
927 | // The lint visitor will visit each subpattern of `p`. We do not want to lint | |
928 | // any range pattern no matter where it occurs in the pattern. For something like | |
929 | // `&(a..=b)`, there is a recursive `check_pat` on `a` and `b`, but we will assume | |
930 | // that if there are unnecessary parens they serve a purpose of readability. | |
931 | PatKind::Range(..) => return, | |
932 | // Avoid `p0 | .. | pn` if we should. | |
933 | PatKind::Or(..) if avoid_or => return, | |
934 | // Avoid `mut x` and `mut x @ p` if we should: | |
f2b60f7d FG |
935 | PatKind::Ident(BindingAnnotation::MUT, ..) if avoid_mut => { |
936 | return; | |
937 | } | |
ba9703b0 XL |
938 | // Otherwise proceed with linting. |
939 | _ => {} | |
940 | } | |
49aad941 FG |
941 | let spans = inner |
942 | .span | |
943 | .find_ancestor_inside(value.span) | |
944 | .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi()))); | |
487cf647 | 945 | self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space); |
ba9703b0 XL |
946 | } |
947 | } | |
948 | } | |
949 | ||
950 | impl EarlyLintPass for UnusedParens { | |
9c376795 | 951 | #[inline] |
ba9703b0 | 952 | fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { |
6a06907d | 953 | match e.kind { |
94222f64 | 954 | ExprKind::Let(ref pat, _, _) | ExprKind::ForLoop(ref pat, ..) => { |
487cf647 | 955 | self.check_unused_parens_pat(cx, pat, false, false, (true, true)); |
6a06907d XL |
956 | } |
957 | // We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already | |
958 | // handle a hard error for them during AST lowering in `lower_expr_mut`, but we still | |
959 | // want to complain about things like `if let 42 = (42)`. | |
960 | ExprKind::If(ref cond, ref block, ref else_) | |
961 | if matches!(cond.peel_parens().kind, ExprKind::Let(..)) => | |
962 | { | |
963 | self.check_unused_delims_expr( | |
964 | cx, | |
965 | cond.peel_parens(), | |
966 | UnusedDelimsCtx::LetScrutineeExpr, | |
967 | true, | |
968 | None, | |
969 | None, | |
970 | ); | |
971 | for stmt in &block.stmts { | |
972 | <Self as UnusedDelimLint>::check_stmt(self, cx, stmt); | |
973 | } | |
974 | if let Some(e) = else_ { | |
975 | <Self as UnusedDelimLint>::check_expr(self, cx, e); | |
976 | } | |
977 | return; | |
978 | } | |
064997fb FG |
979 | ExprKind::Match(ref _expr, ref arm) => { |
980 | for a in arm { | |
981 | self.check_unused_delims_expr( | |
982 | cx, | |
983 | &a.body, | |
984 | UnusedDelimsCtx::MatchArmExpr, | |
985 | false, | |
986 | None, | |
987 | None, | |
988 | ); | |
989 | } | |
990 | } | |
6a06907d | 991 | _ => {} |
ba9703b0 XL |
992 | } |
993 | ||
994 | <Self as UnusedDelimLint>::check_expr(self, cx, e) | |
0bf4aa26 XL |
995 | } |
996 | ||
48663c56 | 997 | fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) { |
dfeec247 | 998 | use ast::{Mutability, PatKind::*}; |
487cf647 | 999 | let keep_space = (false, false); |
e74abb32 | 1000 | match &p.kind { |
e1599b0c XL |
1001 | // Do not lint on `(..)` as that will result in the other arms being useless. |
1002 | Paren(_) | |
1003 | // The other cases do not contain sub-patterns. | |
ba9703b0 | 1004 | | Wild | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => {}, |
e1599b0c | 1005 | // These are list-like patterns; parens can always be removed. |
17df50a5 | 1006 | TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps { |
487cf647 | 1007 | self.check_unused_parens_pat(cx, p, false, false, keep_space); |
e1599b0c | 1008 | }, |
17df50a5 | 1009 | Struct(_, _, fps, _) => for f in fps { |
487cf647 | 1010 | self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space); |
e1599b0c XL |
1011 | }, |
1012 | // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106. | |
487cf647 | 1013 | Ident(.., Some(p)) | Box(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), |
e1599b0c XL |
1014 | // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342. |
1015 | // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106. | |
487cf647 | 1016 | Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), |
0bf4aa26 | 1017 | } |
b039eaaf SL |
1018 | } |
1019 | ||
ba9703b0 XL |
1020 | fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { |
1021 | if let StmtKind::Local(ref local) = s.kind { | |
487cf647 | 1022 | self.check_unused_parens_pat(cx, &local.pat, true, false, (false, false)); |
0bf4aa26 | 1023 | } |
ba9703b0 XL |
1024 | |
1025 | <Self as UnusedDelimLint>::check_stmt(self, cx, s) | |
b039eaaf | 1026 | } |
e1599b0c XL |
1027 | |
1028 | fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) { | |
487cf647 | 1029 | self.check_unused_parens_pat(cx, ¶m.pat, true, false, (false, false)); |
e1599b0c XL |
1030 | } |
1031 | ||
1032 | fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) { | |
487cf647 | 1033 | self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false)); |
e1599b0c | 1034 | } |
e74abb32 XL |
1035 | |
1036 | fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { | |
9c376795 FG |
1037 | match &ty.kind { |
1038 | ast::TyKind::Array(_, len) => { | |
1039 | self.check_unused_delims_expr( | |
1040 | cx, | |
1041 | &len.value, | |
1042 | UnusedDelimsCtx::ArrayLenExpr, | |
1043 | false, | |
1044 | None, | |
1045 | None, | |
1046 | ); | |
1047 | } | |
1048 | ast::TyKind::Paren(r) => { | |
1049 | match &r.kind { | |
1050 | ast::TyKind::TraitObject(..) => {} | |
1051 | ast::TyKind::BareFn(b) | |
1052 | if self.with_self_ty_parens && b.generic_params.len() > 0 => {} | |
1053 | ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {} | |
1054 | _ => { | |
49aad941 FG |
1055 | let spans = r |
1056 | .span | |
1057 | .find_ancestor_inside(ty.span) | |
1058 | .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi()))); | |
1059 | ||
9c376795 FG |
1060 | self.emit_unused_delims(cx, ty.span, spans, "type", (false, false)); |
1061 | } | |
e74abb32 | 1062 | } |
9c376795 | 1063 | self.with_self_ty_parens = false; |
e74abb32 | 1064 | } |
9c376795 | 1065 | _ => {} |
e74abb32 XL |
1066 | } |
1067 | } | |
74b04a01 XL |
1068 | |
1069 | fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { | |
ba9703b0 XL |
1070 | <Self as UnusedDelimLint>::check_item(self, cx, item) |
1071 | } | |
9c376795 FG |
1072 | |
1073 | fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) { | |
1074 | use rustc_ast::{WhereBoundPredicate, WherePredicate}; | |
1075 | if let WherePredicate::BoundPredicate(WhereBoundPredicate { | |
1076 | bounded_ty, | |
1077 | bound_generic_params, | |
1078 | .. | |
1079 | }) = pred && | |
1080 | let ast::TyKind::Paren(_) = &bounded_ty.kind && | |
1081 | bound_generic_params.is_empty() { | |
1082 | self.with_self_ty_parens = true; | |
1083 | } | |
1084 | } | |
1085 | ||
1086 | fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) { | |
1087 | assert!(!self.with_self_ty_parens); | |
1088 | } | |
ba9703b0 | 1089 | } |
74b04a01 | 1090 | |
ba9703b0 | 1091 | declare_lint! { |
1b1a35ee XL |
1092 | /// The `unused_braces` lint detects unnecessary braces around an |
1093 | /// expression. | |
1094 | /// | |
1095 | /// ### Example | |
1096 | /// | |
1097 | /// ```rust | |
1098 | /// if { true } { | |
1099 | /// // ... | |
1100 | /// } | |
1101 | /// ``` | |
1102 | /// | |
1103 | /// {{produces}} | |
1104 | /// | |
1105 | /// ### Explanation | |
1106 | /// | |
1107 | /// The braces are not needed, and should be removed. This is the | |
1108 | /// preferred style for writing these expressions. | |
ba9703b0 XL |
1109 | pub(super) UNUSED_BRACES, |
1110 | Warn, | |
1111 | "unnecessary braces around an expression" | |
1112 | } | |
1113 | ||
1114 | declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]); | |
1115 | ||
1116 | impl UnusedDelimLint for UnusedBraces { | |
1117 | const DELIM_STR: &'static str = "braces"; | |
1118 | ||
1119 | const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false; | |
1120 | ||
1121 | fn lint(&self) -> &'static Lint { | |
1122 | UNUSED_BRACES | |
1123 | } | |
1124 | ||
1125 | fn check_unused_delims_expr( | |
1126 | &self, | |
1127 | cx: &EarlyContext<'_>, | |
1128 | value: &ast::Expr, | |
1129 | ctx: UnusedDelimsCtx, | |
1130 | followed_by_block: bool, | |
1131 | left_pos: Option<BytePos>, | |
1132 | right_pos: Option<BytePos>, | |
1133 | ) { | |
1134 | match value.kind { | |
1135 | ast::ExprKind::Block(ref inner, None) | |
1136 | if inner.rules == ast::BlockCheckMode::Default => | |
1137 | { | |
1138 | // emit a warning under the following conditions: | |
1139 | // | |
1140 | // - the block does not have a label | |
1141 | // - the block is not `unsafe` | |
1142 | // - the block contains exactly one expression (do not lint `{ expr; }`) | |
1143 | // - `followed_by_block` is true and the internal expr may contain a `{` | |
1144 | // - the block is not multiline (do not lint multiline match arms) | |
1145 | // ``` | |
1146 | // match expr { | |
1147 | // Pattern => { | |
1148 | // somewhat_long_expression | |
1149 | // } | |
1150 | // // ... | |
1151 | // } | |
1152 | // ``` | |
1153 | // - the block has no attribute and was not created inside a macro | |
1154 | // - if the block is an `anon_const`, the inner expr must be a literal | |
9c376795 FG |
1155 | // not created by a macro, i.e. do not lint on: |
1156 | // ``` | |
1157 | // struct A<const N: usize>; | |
1158 | // let _: A<{ 2 + 3 }>; | |
1159 | // let _: A<{produces_literal!()}>; | |
1160 | // ``` | |
ba9703b0 XL |
1161 | // FIXME(const_generics): handle paths when #67075 is fixed. |
1162 | if let [stmt] = inner.stmts.as_slice() { | |
1163 | if let ast::StmtKind::Expr(ref expr) = stmt.kind { | |
94222f64 | 1164 | if !Self::is_expr_delims_necessary(expr, followed_by_block, false) |
ba9703b0 | 1165 | && (ctx != UnusedDelimsCtx::AnonConst |
9c376795 FG |
1166 | || (matches!(expr.kind, ast::ExprKind::Lit(_)) |
1167 | && !expr.span.from_expansion())) | |
ba9703b0 XL |
1168 | && !cx.sess().source_map().is_multiline(value.span) |
1169 | && value.attrs.is_empty() | |
1170 | && !value.span.from_expansion() | |
9c376795 | 1171 | && !inner.span.from_expansion() |
ba9703b0 XL |
1172 | { |
1173 | self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos) | |
1174 | } | |
1175 | } | |
1176 | } | |
1177 | } | |
94222f64 | 1178 | ast::ExprKind::Let(_, ref expr, _) => { |
ba9703b0 XL |
1179 | self.check_unused_delims_expr( |
1180 | cx, | |
1181 | expr, | |
1182 | UnusedDelimsCtx::LetScrutineeExpr, | |
1183 | followed_by_block, | |
1184 | None, | |
1185 | None, | |
1186 | ); | |
1187 | } | |
1188 | _ => {} | |
74b04a01 XL |
1189 | } |
1190 | } | |
b039eaaf SL |
1191 | } |
1192 | ||
ba9703b0 | 1193 | impl EarlyLintPass for UnusedBraces { |
29967ef6 XL |
1194 | fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { |
1195 | <Self as UnusedDelimLint>::check_stmt(self, cx, s) | |
1196 | } | |
1197 | ||
9c376795 | 1198 | #[inline] |
ba9703b0 | 1199 | fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { |
29967ef6 XL |
1200 | <Self as UnusedDelimLint>::check_expr(self, cx, e); |
1201 | ||
1202 | if let ExprKind::Repeat(_, ref anon_const) = e.kind { | |
1203 | self.check_unused_delims_expr( | |
1204 | cx, | |
1205 | &anon_const.value, | |
1206 | UnusedDelimsCtx::AnonConst, | |
1207 | false, | |
1208 | None, | |
1209 | None, | |
1210 | ); | |
1211 | } | |
ba9703b0 XL |
1212 | } |
1213 | ||
29967ef6 XL |
1214 | fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) { |
1215 | if let ast::GenericArg::Const(ct) = arg { | |
1216 | self.check_unused_delims_expr( | |
1217 | cx, | |
1218 | &ct.value, | |
1219 | UnusedDelimsCtx::AnonConst, | |
1220 | false, | |
1221 | None, | |
1222 | None, | |
1223 | ); | |
1224 | } | |
ba9703b0 XL |
1225 | } |
1226 | ||
29967ef6 XL |
1227 | fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) { |
1228 | if let Some(anon_const) = &v.disr_expr { | |
1229 | self.check_unused_delims_expr( | |
1230 | cx, | |
1231 | &anon_const.value, | |
1232 | UnusedDelimsCtx::AnonConst, | |
1233 | false, | |
1234 | None, | |
1235 | None, | |
1236 | ); | |
1237 | } | |
ba9703b0 XL |
1238 | } |
1239 | ||
1240 | fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { | |
29967ef6 XL |
1241 | match ty.kind { |
1242 | ast::TyKind::Array(_, ref len) => { | |
ba9703b0 XL |
1243 | self.check_unused_delims_expr( |
1244 | cx, | |
1245 | &len.value, | |
1246 | UnusedDelimsCtx::ArrayLenExpr, | |
1247 | false, | |
1248 | None, | |
1249 | None, | |
1250 | ); | |
1251 | } | |
29967ef6 XL |
1252 | |
1253 | ast::TyKind::Typeof(ref anon_const) => { | |
1254 | self.check_unused_delims_expr( | |
1255 | cx, | |
1256 | &anon_const.value, | |
1257 | UnusedDelimsCtx::AnonConst, | |
1258 | false, | |
1259 | None, | |
1260 | None, | |
1261 | ); | |
1262 | } | |
1263 | ||
1264 | _ => {} | |
ba9703b0 XL |
1265 | } |
1266 | } | |
1267 | ||
1268 | fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { | |
1269 | <Self as UnusedDelimLint>::check_item(self, cx, item) | |
1270 | } | |
1271 | } | |
1272 | ||
b039eaaf | 1273 | declare_lint! { |
1b1a35ee XL |
1274 | /// The `unused_import_braces` lint catches unnecessary braces around an |
1275 | /// imported item. | |
1276 | /// | |
1277 | /// ### Example | |
1278 | /// | |
1279 | /// ```rust,compile_fail | |
1280 | /// #![deny(unused_import_braces)] | |
1281 | /// use test::{A}; | |
1282 | /// | |
1283 | /// pub mod test { | |
1284 | /// pub struct A; | |
1285 | /// } | |
1286 | /// # fn main() {} | |
1287 | /// ``` | |
1288 | /// | |
1289 | /// {{produces}} | |
1290 | /// | |
1291 | /// ### Explanation | |
1292 | /// | |
1293 | /// If there is only a single item, then remove the braces (`use test::A;` | |
1294 | /// for example). | |
1295 | /// | |
1296 | /// This lint is "allow" by default because it is only enforcing a | |
1297 | /// stylistic choice. | |
b039eaaf SL |
1298 | UNUSED_IMPORT_BRACES, |
1299 | Allow, | |
1300 | "unnecessary braces around an imported item" | |
1301 | } | |
1302 | ||
532ac7d7 | 1303 | declare_lint_pass!(UnusedImportBraces => [UNUSED_IMPORT_BRACES]); |
b039eaaf | 1304 | |
ff7c6d11 | 1305 | impl UnusedImportBraces { |
9fa01778 | 1306 | fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) { |
ff7c6d11 XL |
1307 | if let ast::UseTreeKind::Nested(ref items) = use_tree.kind { |
1308 | // Recursively check nested UseTrees | |
9c376795 | 1309 | for (tree, _) in items { |
ff7c6d11 XL |
1310 | self.check_use_tree(cx, tree, item); |
1311 | } | |
1312 | ||
1313 | // Trigger the lint only if there is one nested item | |
1314 | if items.len() != 1 { | |
1315 | return; | |
1316 | } | |
1317 | ||
1318 | // Trigger the lint if the nested item is a non-self single item | |
e1599b0c | 1319 | let node_name = match items[0].0.kind { |
487cf647 | 1320 | ast::UseTreeKind::Simple(rename) => { |
83c7162d | 1321 | let orig_ident = items[0].0.prefix.segments.last().unwrap().ident; |
dc9dc135 | 1322 | if orig_ident.name == kw::SelfLower { |
ff7c6d11 | 1323 | return; |
ff7c6d11 | 1324 | } |
e1599b0c | 1325 | rename.unwrap_or(orig_ident).name |
ff7c6d11 | 1326 | } |
e1599b0c XL |
1327 | ast::UseTreeKind::Glob => Symbol::intern("*"), |
1328 | ast::UseTreeKind::Nested(_) => return, | |
1329 | }; | |
ff7c6d11 | 1330 | |
9c376795 | 1331 | cx.emit_spanned_lint( |
2b03887a FG |
1332 | UNUSED_IMPORT_BRACES, |
1333 | item.span, | |
9c376795 | 1334 | UnusedImportBracesDiag { node: node_name }, |
2b03887a | 1335 | ); |
ff7c6d11 XL |
1336 | } |
1337 | } | |
1338 | } | |
1339 | ||
476ff2be | 1340 | impl EarlyLintPass for UnusedImportBraces { |
9fa01778 | 1341 | fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { |
e74abb32 | 1342 | if let ast::ItemKind::Use(ref use_tree) = item.kind { |
ff7c6d11 | 1343 | self.check_use_tree(cx, use_tree, item); |
b039eaaf SL |
1344 | } |
1345 | } | |
1346 | } | |
1347 | ||
1348 | declare_lint! { | |
1b1a35ee XL |
1349 | /// The `unused_allocation` lint detects unnecessary allocations that can |
1350 | /// be eliminated. | |
1351 | /// | |
1352 | /// ### Example | |
1353 | /// | |
1354 | /// ```rust | |
1b1a35ee | 1355 | /// fn main() { |
353b0b11 | 1356 | /// let a = Box::new([1, 2, 3]).len(); |
1b1a35ee XL |
1357 | /// } |
1358 | /// ``` | |
1359 | /// | |
1360 | /// {{produces}} | |
1361 | /// | |
1362 | /// ### Explanation | |
1363 | /// | |
1364 | /// When a `box` expression is immediately coerced to a reference, then | |
1365 | /// the allocation is unnecessary, and a reference (using `&` or `&mut`) | |
1366 | /// should be used instead to avoid the allocation. | |
abe05a73 | 1367 | pub(super) UNUSED_ALLOCATION, |
b039eaaf SL |
1368 | Warn, |
1369 | "detects unnecessary allocations that can be eliminated" | |
1370 | } | |
1371 | ||
532ac7d7 | 1372 | declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]); |
b039eaaf | 1373 | |
f035d41b XL |
1374 | impl<'tcx> LateLintPass<'tcx> for UnusedAllocation { |
1375 | fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) { | |
e74abb32 | 1376 | match e.kind { |
353b0b11 FG |
1377 | hir::ExprKind::Call(path_expr, [_]) |
1378 | if let hir::ExprKind::Path(qpath) = &path_expr.kind | |
1379 | && let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() | |
1380 | && cx.tcx.is_diagnostic_item(sym::box_new, did) | |
1381 | => {} | |
c30ab7b3 | 1382 | _ => return, |
b039eaaf SL |
1383 | } |
1384 | ||
3dfed10e | 1385 | for adj in cx.typeck_results().expr_adjustments(e) { |
7cac9316 | 1386 | if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind { |
9c376795 FG |
1387 | match m { |
1388 | adjustment::AutoBorrowMutability::Not => { | |
1389 | cx.emit_spanned_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag); | |
1390 | } | |
1391 | adjustment::AutoBorrowMutability::Mut { .. } => { | |
1392 | cx.emit_spanned_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag); | |
1393 | } | |
1394 | }; | |
b039eaaf SL |
1395 | } |
1396 | } | |
1397 | } | |
1398 | } |