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