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