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