]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/utils/internal_lints.rs
Ignore associated items in trait *implementations* when considering type complexity
[rust.git] / clippy_lints / src / utils / internal_lints.rs
1 use clippy_utils::consts::{constant_simple, Constant};
2 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
3 use clippy_utils::higher;
4 use clippy_utils::source::snippet;
5 use clippy_utils::ty::match_type;
6 use clippy_utils::{
7     is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls, path_to_res,
8     paths, SpanlessEq,
9 };
10 use if_chain::if_chain;
11 use rustc_ast as ast;
12 use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId};
13 use rustc_ast::visit::FnKind;
14 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
15 use rustc_errors::Applicability;
16 use rustc_hir as hir;
17 use rustc_hir::def::{DefKind, Res};
18 use rustc_hir::def_id::DefId;
19 use rustc_hir::hir_id::CRATE_HIR_ID;
20 use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
21 use rustc_hir::{
22     BinOpKind, Block, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, TyKind,
23     UnOp,
24 };
25 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
26 use rustc_middle::hir::map::Map;
27 use rustc_middle::mir::interpret::ConstValue;
28 use rustc_middle::ty;
29 use rustc_semver::RustcVersion;
30 use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
31 use rustc_span::source_map::Spanned;
32 use rustc_span::symbol::{Symbol, SymbolStr};
33 use rustc_span::{sym, BytePos, Span};
34 use rustc_typeck::hir_ty_to_ty;
35
36 use std::borrow::{Borrow, Cow};
37
38 #[cfg(feature = "metadata-collector-lint")]
39 pub mod metadata_collector;
40
41 declare_clippy_lint! {
42     /// ### What it does
43     /// Checks for various things we like to keep tidy in clippy.
44     ///
45     /// ### Why is this bad?
46     /// We like to pretend we're an example of tidy code.
47     ///
48     /// ### Example
49     /// Wrong ordering of the util::paths constants.
50     pub CLIPPY_LINTS_INTERNAL,
51     internal,
52     "various things that will negatively affect your clippy experience"
53 }
54
55 declare_clippy_lint! {
56     /// ### What it does
57     /// Ensures every lint is associated to a `LintPass`.
58     ///
59     /// ### Why is this bad?
60     /// The compiler only knows lints via a `LintPass`. Without
61     /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
62     /// know the name of the lint.
63     ///
64     /// ### Known problems
65     /// Only checks for lints associated using the
66     /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
67     ///
68     /// ### Example
69     /// ```rust,ignore
70     /// declare_lint! { pub LINT_1, ... }
71     /// declare_lint! { pub LINT_2, ... }
72     /// declare_lint! { pub FORGOTTEN_LINT, ... }
73     /// // ...
74     /// declare_lint_pass!(Pass => [LINT_1, LINT_2]);
75     /// // missing FORGOTTEN_LINT
76     /// ```
77     pub LINT_WITHOUT_LINT_PASS,
78     internal,
79     "declaring a lint without associating it in a LintPass"
80 }
81
82 declare_clippy_lint! {
83     /// ### What it does
84     /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
85     /// variant of the function.
86     ///
87     /// ### Why is this bad?
88     /// The `utils::*` variants also add a link to the Clippy documentation to the
89     /// warning/error messages.
90     ///
91     /// ### Example
92     /// Bad:
93     /// ```rust,ignore
94     /// cx.span_lint(LINT_NAME, "message");
95     /// ```
96     ///
97     /// Good:
98     /// ```rust,ignore
99     /// utils::span_lint(cx, LINT_NAME, "message");
100     /// ```
101     pub COMPILER_LINT_FUNCTIONS,
102     internal,
103     "usage of the lint functions of the compiler instead of the utils::* variant"
104 }
105
106 declare_clippy_lint! {
107     /// ### What it does
108     /// Checks for calls to `cx.outer().expn_data()` and suggests to use
109     /// the `cx.outer_expn_data()`
110     ///
111     /// ### Why is this bad?
112     /// `cx.outer_expn_data()` is faster and more concise.
113     ///
114     /// ### Example
115     /// Bad:
116     /// ```rust,ignore
117     /// expr.span.ctxt().outer().expn_data()
118     /// ```
119     ///
120     /// Good:
121     /// ```rust,ignore
122     /// expr.span.ctxt().outer_expn_data()
123     /// ```
124     pub OUTER_EXPN_EXPN_DATA,
125     internal,
126     "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
127 }
128
129 declare_clippy_lint! {
130     /// ### What it does
131     /// Not an actual lint. This lint is only meant for testing our customized internal compiler
132     /// error message by calling `panic`.
133     ///
134     /// ### Why is this bad?
135     /// ICE in large quantities can damage your teeth
136     ///
137     /// ### Example
138     /// Bad:
139     /// ```rust,ignore
140     /// 🍦🍦🍦🍦🍦
141     /// ```
142     pub PRODUCE_ICE,
143     internal,
144     "this message should not appear anywhere as we ICE before and don't emit the lint"
145 }
146
147 declare_clippy_lint! {
148     /// ### What it does
149     /// Checks for cases of an auto-generated lint without an updated description,
150     /// i.e. `default lint description`.
151     ///
152     /// ### Why is this bad?
153     /// Indicates that the lint is not finished.
154     ///
155     /// ### Example
156     /// Bad:
157     /// ```rust,ignore
158     /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
159     /// ```
160     ///
161     /// Good:
162     /// ```rust,ignore
163     /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
164     /// ```
165     pub DEFAULT_LINT,
166     internal,
167     "found 'default lint description' in a lint declaration"
168 }
169
170 declare_clippy_lint! {
171     /// ### What it does
172     /// Lints `span_lint_and_then` function calls, where the
173     /// closure argument has only one statement and that statement is a method
174     /// call to `span_suggestion`, `span_help`, `span_note` (using the same
175     /// span), `help` or `note`.
176     ///
177     /// These usages of `span_lint_and_then` should be replaced with one of the
178     /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
179     /// `span_lint_and_note`.
180     ///
181     /// ### Why is this bad?
182     /// Using the wrapper `span_lint_and_*` functions, is more
183     /// convenient, readable and less error prone.
184     ///
185     /// ### Example
186     /// Bad:
187     /// ```rust,ignore
188     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
189     ///     diag.span_suggestion(
190     ///         expr.span,
191     ///         help_msg,
192     ///         sugg.to_string(),
193     ///         Applicability::MachineApplicable,
194     ///     );
195     /// });
196     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
197     ///     diag.span_help(expr.span, help_msg);
198     /// });
199     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
200     ///     diag.help(help_msg);
201     /// });
202     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
203     ///     diag.span_note(expr.span, note_msg);
204     /// });
205     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
206     ///     diag.note(note_msg);
207     /// });
208     /// ```
209     ///
210     /// Good:
211     /// ```rust,ignore
212     /// span_lint_and_sugg(
213     ///     cx,
214     ///     TEST_LINT,
215     ///     expr.span,
216     ///     lint_msg,
217     ///     help_msg,
218     ///     sugg.to_string(),
219     ///     Applicability::MachineApplicable,
220     /// );
221     /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
222     /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
223     /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
224     /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
225     /// ```
226     pub COLLAPSIBLE_SPAN_LINT_CALLS,
227     internal,
228     "found collapsible `span_lint_and_then` calls"
229 }
230
231 declare_clippy_lint! {
232     /// ### What it does
233     /// Checks for calls to `utils::match_type()` on a type diagnostic item
234     /// and suggests to use `utils::is_type_diagnostic_item()` instead.
235     ///
236     /// ### Why is this bad?
237     /// `utils::is_type_diagnostic_item()` does not require hardcoded paths.
238     ///
239     /// ### Example
240     /// Bad:
241     /// ```rust,ignore
242     /// utils::match_type(cx, ty, &paths::VEC)
243     /// ```
244     ///
245     /// Good:
246     /// ```rust,ignore
247     /// utils::is_type_diagnostic_item(cx, ty, sym::Vec)
248     /// ```
249     pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
250     internal,
251     "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
252 }
253
254 declare_clippy_lint! {
255     /// ### What it does
256     /// Checks the paths module for invalid paths.
257     ///
258     /// ### Why is this bad?
259     /// It indicates a bug in the code.
260     ///
261     /// ### Example
262     /// None.
263     pub INVALID_PATHS,
264     internal,
265     "invalid path"
266 }
267
268 declare_clippy_lint! {
269     /// ### What it does
270     /// Checks for interning symbols that have already been pre-interned and defined as constants.
271     ///
272     /// ### Why is this bad?
273     /// It's faster and easier to use the symbol constant.
274     ///
275     /// ### Example
276     /// Bad:
277     /// ```rust,ignore
278     /// let _ = sym!(f32);
279     /// ```
280     ///
281     /// Good:
282     /// ```rust,ignore
283     /// let _ = sym::f32;
284     /// ```
285     pub INTERNING_DEFINED_SYMBOL,
286     internal,
287     "interning a symbol that is pre-interned and defined as a constant"
288 }
289
290 declare_clippy_lint! {
291     /// ### What it does
292     /// Checks for unnecessary conversion from Symbol to a string.
293     ///
294     /// ### Why is this bad?
295     /// It's faster use symbols directly intead of strings.
296     ///
297     /// ### Example
298     /// Bad:
299     /// ```rust,ignore
300     /// symbol.as_str() == "clippy";
301     /// ```
302     ///
303     /// Good:
304     /// ```rust,ignore
305     /// symbol == sym::clippy;
306     /// ```
307     pub UNNECESSARY_SYMBOL_STR,
308     internal,
309     "unnecessary conversion between Symbol and string"
310 }
311
312 declare_clippy_lint! {
313     /// Finds unidiomatic usage of `if_chain!`
314     pub IF_CHAIN_STYLE,
315     internal,
316     "non-idiomatic `if_chain!` usage"
317 }
318
319 declare_clippy_lint! {
320     /// ### What it does
321     /// Checks for invalid `clippy::version` attributes.
322     ///
323     /// Valid values are:
324     /// * "pre 1.29.0"
325     /// * any valid semantic version
326     pub INVALID_CLIPPY_VERSION_ATTRIBUTE,
327     internal,
328     "found an invalid `clippy::version` attribute"
329 }
330
331 declare_clippy_lint! {
332     /// ### What it does
333     /// Checks for declared clippy lints without the `clippy::version` attribute.
334     ///
335     pub MISSING_CLIPPY_VERSION_ATTRIBUTE,
336     internal,
337     "found clippy lint without `clippy::version` attribute"
338 }
339
340 declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
341
342 impl EarlyLintPass for ClippyLintsInternal {
343     fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
344         if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
345             if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
346                 if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
347                     if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
348                         let mut last_name: Option<SymbolStr> = None;
349                         for item in items {
350                             let name = item.ident.as_str();
351                             if let Some(ref last_name) = last_name {
352                                 if **last_name > *name {
353                                     span_lint(
354                                         cx,
355                                         CLIPPY_LINTS_INTERNAL,
356                                         item.span,
357                                         "this constant should be before the previous constant due to lexical \
358                                          ordering",
359                                     );
360                                 }
361                             }
362                             last_name = Some(name);
363                         }
364                     }
365                 }
366             }
367         }
368     }
369 }
370
371 #[derive(Clone, Debug, Default)]
372 pub struct LintWithoutLintPass {
373     declared_lints: FxHashMap<Symbol, Span>,
374     registered_lints: FxHashSet<Symbol>,
375 }
376
377 impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]);
378
379 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
380     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
381         if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) {
382             return;
383         }
384
385         if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
386             if is_lint_ref_type(cx, ty) {
387                 check_invalid_clippy_version_attribute(cx, item);
388
389                 let expr = &cx.tcx.hir().body(body_id).value;
390                 if_chain! {
391                     if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind;
392                     if let ExprKind::Struct(_, fields, _) = inner_exp.kind;
393                     let field = fields
394                         .iter()
395                         .find(|f| f.ident.as_str() == "desc")
396                         .expect("lints must have a description field");
397                     if let ExprKind::Lit(Spanned {
398                         node: LitKind::Str(ref sym, _),
399                         ..
400                     }) = field.expr.kind;
401                     if sym.as_str() == "default lint description";
402
403                     then {
404                         span_lint(
405                             cx,
406                             DEFAULT_LINT,
407                             item.span,
408                             &format!("the lint `{}` has the default lint description", item.ident.name),
409                         );
410                     }
411                 }
412                 self.declared_lints.insert(item.ident.name, item.span);
413             }
414         } else if is_expn_of(item.span, "impl_lint_pass").is_some()
415             || is_expn_of(item.span, "declare_lint_pass").is_some()
416         {
417             if let hir::ItemKind::Impl(hir::Impl {
418                 of_trait: None,
419                 items: impl_item_refs,
420                 ..
421             }) = item.kind
422             {
423                 let mut collector = LintCollector {
424                     output: &mut self.registered_lints,
425                     cx,
426                 };
427                 let body_id = cx.tcx.hir().body_owned_by(
428                     impl_item_refs
429                         .iter()
430                         .find(|iiref| iiref.ident.as_str() == "get_lints")
431                         .expect("LintPass needs to implement get_lints")
432                         .id
433                         .hir_id(),
434                 );
435                 collector.visit_expr(&cx.tcx.hir().body(body_id).value);
436             }
437         }
438     }
439
440     fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
441         if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
442             return;
443         }
444
445         for (lint_name, &lint_span) in &self.declared_lints {
446             // When using the `declare_tool_lint!` macro, the original `lint_span`'s
447             // file points to "<rustc macros>".
448             // `compiletest-rs` thinks that's an error in a different file and
449             // just ignores it. This causes the test in compile-fail/lint_pass
450             // not able to capture the error.
451             // Therefore, we need to climb the macro expansion tree and find the
452             // actual span that invoked `declare_tool_lint!`:
453             let lint_span = lint_span.ctxt().outer_expn_data().call_site;
454
455             if !self.registered_lints.contains(lint_name) {
456                 span_lint(
457                     cx,
458                     LINT_WITHOUT_LINT_PASS,
459                     lint_span,
460                     &format!("the lint `{}` is not added to any `LintPass`", lint_name),
461                 );
462             }
463         }
464     }
465 }
466
467 fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool {
468     if let TyKind::Rptr(
469         _,
470         MutTy {
471             ty: inner,
472             mutbl: Mutability::Not,
473         },
474     ) = ty.kind
475     {
476         if let TyKind::Path(ref path) = inner.kind {
477             if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
478                 return match_def_path(cx, def_id, &paths::LINT);
479             }
480         }
481     }
482
483     false
484 }
485
486 fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
487     if let Some(value) = extract_clippy_version_value(cx, item) {
488         // The `sym!` macro doesn't work as it only expects a single token.
489         // It's better to keep it this way and have a direct `Symbol::intern` call here.
490         if value == Symbol::intern("pre 1.29.0") {
491             return;
492         }
493
494         if RustcVersion::parse(&*value.as_str()).is_err() {
495             span_lint_and_help(
496                 cx,
497                 INVALID_CLIPPY_VERSION_ATTRIBUTE,
498                 item.span,
499                 "this item has an invalid `clippy::version` attribute",
500                 None,
501                 "please use a valid sematic version, see `doc/adding_lints.md`",
502             );
503         }
504     } else {
505         span_lint_and_help(
506             cx,
507             MISSING_CLIPPY_VERSION_ATTRIBUTE,
508             item.span,
509             "this lint is missing the `clippy::version` attribute or version value",
510             None,
511             "please use a `clippy::version` attribute, see `doc/adding_lints.md`",
512         );
513     }
514 }
515
516 /// This function extracts the version value of a `clippy::version` attribute if the given value has
517 /// one
518 fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
519     let attrs = cx.tcx.hir().attrs(item.hir_id());
520     attrs.iter().find_map(|attr| {
521         if_chain! {
522             // Identify attribute
523             if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind;
524             if let [tool_name, attr_name] = &attr_kind.path.segments[..];
525             if tool_name.ident.name == sym::clippy;
526             if attr_name.ident.name == sym::version;
527             if let Some(version) = attr.value_str();
528             then {
529                 Some(version)
530             } else {
531                 None
532             }
533         }
534     })
535 }
536
537 struct LintCollector<'a, 'tcx> {
538     output: &'a mut FxHashSet<Symbol>,
539     cx: &'a LateContext<'tcx>,
540 }
541
542 impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
543     type Map = Map<'tcx>;
544
545     fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
546         if path.segments.len() == 1 {
547             self.output.insert(path.segments[0].ident.name);
548         }
549     }
550
551     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
552         NestedVisitorMap::All(self.cx.tcx.hir())
553     }
554 }
555
556 #[derive(Clone, Default)]
557 pub struct CompilerLintFunctions {
558     map: FxHashMap<&'static str, &'static str>,
559 }
560
561 impl CompilerLintFunctions {
562     #[must_use]
563     pub fn new() -> Self {
564         let mut map = FxHashMap::default();
565         map.insert("span_lint", "utils::span_lint");
566         map.insert("struct_span_lint", "utils::span_lint");
567         map.insert("lint", "utils::span_lint");
568         map.insert("span_lint_note", "utils::span_lint_and_note");
569         map.insert("span_lint_help", "utils::span_lint_and_help");
570         Self { map }
571     }
572 }
573
574 impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
575
576 impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
577     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
578         if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
579             return;
580         }
581
582         if_chain! {
583             if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;
584             let fn_name = path.ident;
585             if let Some(sugg) = self.map.get(&*fn_name.as_str());
586             let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
587             if match_type(cx, ty, &paths::EARLY_CONTEXT)
588                 || match_type(cx, ty, &paths::LATE_CONTEXT);
589             then {
590                 span_lint_and_help(
591                     cx,
592                     COMPILER_LINT_FUNCTIONS,
593                     path.ident.span,
594                     "usage of a compiler lint function",
595                     None,
596                     &format!("please use the Clippy variant of this function: `{}`", sugg),
597                 );
598             }
599         }
600     }
601 }
602
603 declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
604
605 impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
606     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
607         if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
608             return;
609         }
610
611         let (method_names, arg_lists, spans) = method_calls(expr, 2);
612         let method_names: Vec<SymbolStr> = method_names.iter().map(|s| s.as_str()).collect();
613         let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect();
614         if_chain! {
615             if let ["expn_data", "outer_expn"] = method_names.as_slice();
616             let args = arg_lists[1];
617             if args.len() == 1;
618             let self_arg = &args[0];
619             let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
620             if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
621             then {
622                 span_lint_and_sugg(
623                     cx,
624                     OUTER_EXPN_EXPN_DATA,
625                     spans[1].with_hi(expr.span.hi()),
626                     "usage of `outer_expn().expn_data()`",
627                     "try",
628                     "outer_expn_data()".to_string(),
629                     Applicability::MachineApplicable,
630                 );
631             }
632         }
633     }
634 }
635
636 declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
637
638 impl EarlyLintPass for ProduceIce {
639     fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
640         assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
641     }
642 }
643
644 fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
645     match fn_kind {
646         FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
647         FnKind::Closure(..) => false,
648     }
649 }
650
651 declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
652
653 impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
654     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
655         if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
656             return;
657         }
658
659         if_chain! {
660             if let ExprKind::Call(func, and_then_args) = expr.kind;
661             if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
662             if and_then_args.len() == 5;
663             if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
664             let body = cx.tcx.hir().body(*body_id);
665             if let ExprKind::Block(block, _) = &body.value.kind;
666             let stmts = &block.stmts;
667             if stmts.len() == 1 && block.expr.is_none();
668             if let StmtKind::Semi(only_expr) = &stmts[0].kind;
669             if let ExprKind::MethodCall(ps, _, span_call_args, _) = &only_expr.kind;
670             then {
671                 let and_then_snippets = get_and_then_snippets(cx, and_then_args);
672                 let mut sle = SpanlessEq::new(cx).deny_side_effects();
673                 match &*ps.ident.as_str() {
674                     "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
675                         suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
676                     },
677                     "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
678                         let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
679                         suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
680                     },
681                     "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
682                         let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
683                         suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
684                     },
685                     "help" => {
686                         let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
687                         suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
688                     }
689                     "note" => {
690                         let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
691                         suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
692                     }
693                     _  => (),
694                 }
695             }
696         }
697     }
698 }
699
700 struct AndThenSnippets<'a> {
701     cx: Cow<'a, str>,
702     lint: Cow<'a, str>,
703     span: Cow<'a, str>,
704     msg: Cow<'a, str>,
705 }
706
707 fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> {
708     let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx");
709     let lint_snippet = snippet(cx, and_then_snippets[1].span, "..");
710     let span_snippet = snippet(cx, and_then_snippets[2].span, "span");
711     let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#);
712
713     AndThenSnippets {
714         cx: cx_snippet,
715         lint: lint_snippet,
716         span: span_snippet,
717         msg: msg_snippet,
718     }
719 }
720
721 struct SpanSuggestionSnippets<'a> {
722     help: Cow<'a, str>,
723     sugg: Cow<'a, str>,
724     applicability: Cow<'a, str>,
725 }
726
727 fn span_suggestion_snippets<'a, 'hir>(
728     cx: &LateContext<'_>,
729     span_call_args: &'hir [Expr<'hir>],
730 ) -> SpanSuggestionSnippets<'a> {
731     let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
732     let sugg_snippet = snippet(cx, span_call_args[3].span, "..");
733     let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable");
734
735     SpanSuggestionSnippets {
736         help: help_snippet,
737         sugg: sugg_snippet,
738         applicability: applicability_snippet,
739     }
740 }
741
742 fn suggest_suggestion(
743     cx: &LateContext<'_>,
744     expr: &Expr<'_>,
745     and_then_snippets: &AndThenSnippets<'_>,
746     span_suggestion_snippets: &SpanSuggestionSnippets<'_>,
747 ) {
748     span_lint_and_sugg(
749         cx,
750         COLLAPSIBLE_SPAN_LINT_CALLS,
751         expr.span,
752         "this call is collapsible",
753         "collapse into",
754         format!(
755             "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})",
756             and_then_snippets.cx,
757             and_then_snippets.lint,
758             and_then_snippets.span,
759             and_then_snippets.msg,
760             span_suggestion_snippets.help,
761             span_suggestion_snippets.sugg,
762             span_suggestion_snippets.applicability
763         ),
764         Applicability::MachineApplicable,
765     );
766 }
767
768 fn suggest_help(
769     cx: &LateContext<'_>,
770     expr: &Expr<'_>,
771     and_then_snippets: &AndThenSnippets<'_>,
772     help: &str,
773     with_span: bool,
774 ) {
775     let option_span = if with_span {
776         format!("Some({})", and_then_snippets.span)
777     } else {
778         "None".to_string()
779     };
780
781     span_lint_and_sugg(
782         cx,
783         COLLAPSIBLE_SPAN_LINT_CALLS,
784         expr.span,
785         "this call is collapsible",
786         "collapse into",
787         format!(
788             "span_lint_and_help({}, {}, {}, {}, {}, {})",
789             and_then_snippets.cx,
790             and_then_snippets.lint,
791             and_then_snippets.span,
792             and_then_snippets.msg,
793             &option_span,
794             help
795         ),
796         Applicability::MachineApplicable,
797     );
798 }
799
800 fn suggest_note(
801     cx: &LateContext<'_>,
802     expr: &Expr<'_>,
803     and_then_snippets: &AndThenSnippets<'_>,
804     note: &str,
805     with_span: bool,
806 ) {
807     let note_span = if with_span {
808         format!("Some({})", and_then_snippets.span)
809     } else {
810         "None".to_string()
811     };
812
813     span_lint_and_sugg(
814         cx,
815         COLLAPSIBLE_SPAN_LINT_CALLS,
816         expr.span,
817         "this call is collspible",
818         "collapse into",
819         format!(
820             "span_lint_and_note({}, {}, {}, {}, {}, {})",
821             and_then_snippets.cx,
822             and_then_snippets.lint,
823             and_then_snippets.span,
824             and_then_snippets.msg,
825             note_span,
826             note
827         ),
828         Applicability::MachineApplicable,
829     );
830 }
831
832 declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]);
833
834 impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
835     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
836         if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) {
837             return;
838         }
839
840         if_chain! {
841             // Check if this is a call to utils::match_type()
842             if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
843             if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
844             // Extract the path to the matched type
845             if let Some(segments) = path_to_matched_type(cx, ty_path);
846             let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
847             if let Some(ty_did) = path_to_res(cx, &segments[..]).opt_def_id();
848             // Check if the matched type is a diagnostic item
849             if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did);
850             then {
851                 // TODO: check paths constants from external crates.
852                 let cx_snippet = snippet(cx, context.span, "_");
853                 let ty_snippet = snippet(cx, ty.span, "_");
854
855                 span_lint_and_sugg(
856                     cx,
857                     MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
858                     expr.span,
859                     "usage of `clippy_utils::ty::match_type()` on a type diagnostic item",
860                     "try",
861                     format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
862                     Applicability::MaybeIncorrect,
863                 );
864             }
865         }
866     }
867 }
868
869 fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<SymbolStr>> {
870     use rustc_hir::ItemKind;
871
872     match &expr.kind {
873         ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
874         ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) {
875             Res::Local(hir_id) => {
876                 let parent_id = cx.tcx.hir().get_parent_node(hir_id);
877                 if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) {
878                     if let Some(init) = local.init {
879                         return path_to_matched_type(cx, init);
880                     }
881                 }
882             },
883             Res::Def(DefKind::Const | DefKind::Static, def_id) => {
884                 if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
885                     if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
886                         let body = cx.tcx.hir().body(body_id);
887                         return path_to_matched_type(cx, &body.value);
888                     }
889                 }
890             },
891             _ => {},
892         },
893         ExprKind::Array(exprs) => {
894             let segments: Vec<SymbolStr> = exprs
895                 .iter()
896                 .filter_map(|expr| {
897                     if let ExprKind::Lit(lit) = &expr.kind {
898                         if let LitKind::Str(sym, _) = lit.node {
899                             return Some(sym.as_str());
900                         }
901                     }
902
903                     None
904                 })
905                 .collect();
906
907             if segments.len() == exprs.len() {
908                 return Some(segments);
909             }
910         },
911         _ => {},
912     }
913
914     None
915 }
916
917 // This is not a complete resolver for paths. It works on all the paths currently used in the paths
918 // module.  That's all it does and all it needs to do.
919 pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
920     if path_to_res(cx, path) != Res::Err {
921         return true;
922     }
923
924     // Some implementations can't be found by `path_to_res`, particularly inherent
925     // implementations of native types. Check lang items.
926     let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
927     let lang_items = cx.tcx.lang_items();
928     for item_def_id in lang_items.items().iter().flatten() {
929         let lang_item_path = cx.get_def_path(*item_def_id);
930         if path_syms.starts_with(&lang_item_path) {
931             if let [item] = &path_syms[lang_item_path.len()..] {
932                 for child in cx.tcx.item_children(*item_def_id) {
933                     if child.ident.name == *item {
934                         return true;
935                     }
936                 }
937             }
938         }
939     }
940
941     false
942 }
943
944 declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
945
946 impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
947     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
948         let local_def_id = &cx.tcx.parent_module(item.hir_id());
949         let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
950         if_chain! {
951             if mod_name.as_str() == "paths";
952             if let hir::ItemKind::Const(ty, body_id) = item.kind;
953             let ty = hir_ty_to_ty(cx.tcx, ty);
954             if let ty::Array(el_ty, _) = &ty.kind();
955             if let ty::Ref(_, el_ty, _) = &el_ty.kind();
956             if el_ty.is_str();
957             let body = cx.tcx.hir().body(body_id);
958             let typeck_results = cx.tcx.typeck_body(body_id);
959             if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value);
960             let path: Vec<&str> = path.iter().map(|x| {
961                     if let Constant::Str(s) = x {
962                         s.as_str()
963                     } else {
964                         // We checked the type of the constant above
965                         unreachable!()
966                     }
967                 }).collect();
968             if !check_path(cx, &path[..]);
969             then {
970                 span_lint(cx, INVALID_PATHS, item.span, "invalid path");
971             }
972         }
973     }
974 }
975
976 #[derive(Default)]
977 pub struct InterningDefinedSymbol {
978     // Maps the symbol value to the constant DefId.
979     symbol_map: FxHashMap<u32, DefId>,
980 }
981
982 impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]);
983
984 impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
985     fn check_crate(&mut self, cx: &LateContext<'_>) {
986         if !self.symbol_map.is_empty() {
987             return;
988         }
989
990         for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
991             if let Some(def_id) = path_to_res(cx, module).opt_def_id() {
992                 for item in cx.tcx.item_children(def_id).iter() {
993                     if_chain! {
994                         if let Res::Def(DefKind::Const, item_def_id) = item.res;
995                         let ty = cx.tcx.type_of(item_def_id);
996                         if match_type(cx, ty, &paths::SYMBOL);
997                         if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
998                         if let Ok(value) = value.to_u32();
999                         then {
1000                             self.symbol_map.insert(value, item_def_id);
1001                         }
1002                     }
1003                 }
1004             }
1005         }
1006     }
1007
1008     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1009         if_chain! {
1010             if let ExprKind::Call(func, [arg]) = &expr.kind;
1011             if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
1012             if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
1013             if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
1014             let value = Symbol::intern(&arg).as_u32();
1015             if let Some(&def_id) = self.symbol_map.get(&value);
1016             then {
1017                 span_lint_and_sugg(
1018                     cx,
1019                     INTERNING_DEFINED_SYMBOL,
1020                     is_expn_of(expr.span, "sym").unwrap_or(expr.span),
1021                     "interning a defined symbol",
1022                     "try",
1023                     cx.tcx.def_path_str(def_id),
1024                     Applicability::MachineApplicable,
1025                 );
1026             }
1027         }
1028         if let ExprKind::Binary(op, left, right) = expr.kind {
1029             if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) {
1030                 let data = [
1031                     (left, self.symbol_str_expr(left, cx)),
1032                     (right, self.symbol_str_expr(right, cx)),
1033                 ];
1034                 match data {
1035                     // both operands are a symbol string
1036                     [(_, Some(left)), (_, Some(right))] => {
1037                         span_lint_and_sugg(
1038                             cx,
1039                             UNNECESSARY_SYMBOL_STR,
1040                             expr.span,
1041                             "unnecessary `Symbol` to string conversion",
1042                             "try",
1043                             format!(
1044                                 "{} {} {}",
1045                                 left.as_symbol_snippet(cx),
1046                                 op.node.as_str(),
1047                                 right.as_symbol_snippet(cx),
1048                             ),
1049                             Applicability::MachineApplicable,
1050                         );
1051                     },
1052                     // one of the operands is a symbol string
1053                     [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => {
1054                         // creating an owned string for comparison
1055                         if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) {
1056                             span_lint_and_sugg(
1057                                 cx,
1058                                 UNNECESSARY_SYMBOL_STR,
1059                                 expr.span,
1060                                 "unnecessary string allocation",
1061                                 "try",
1062                                 format!("{}.as_str()", symbol.as_symbol_snippet(cx)),
1063                                 Applicability::MachineApplicable,
1064                             );
1065                         }
1066                     },
1067                     // nothing found
1068                     [(_, None), (_, None)] => {},
1069                 }
1070             }
1071         }
1072     }
1073 }
1074
1075 impl InterningDefinedSymbol {
1076     fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> {
1077         static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD];
1078         static SYMBOL_STR_PATHS: &[&[&str]] = &[
1079             &paths::SYMBOL_AS_STR,
1080             &paths::SYMBOL_TO_IDENT_STRING,
1081             &paths::TO_STRING_METHOD,
1082         ];
1083         // SymbolStr might be de-referenced: `&*symbol.as_str()`
1084         let call = if_chain! {
1085             if let ExprKind::AddrOf(_, _, e) = expr.kind;
1086             if let ExprKind::Unary(UnOp::Deref, e) = e.kind;
1087             then { e } else { expr }
1088         };
1089         if_chain! {
1090             // is a method call
1091             if let ExprKind::MethodCall(_, _, [item], _) = call.kind;
1092             if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
1093             let ty = cx.typeck_results().expr_ty(item);
1094             // ...on either an Ident or a Symbol
1095             if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) {
1096                 Some(false)
1097             } else if match_type(cx, ty, &paths::IDENT) {
1098                 Some(true)
1099             } else {
1100                 None
1101             };
1102             // ...which converts it to a string
1103             let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS };
1104             if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path));
1105             then {
1106                 let is_to_owned = path.last().unwrap().ends_with("string");
1107                 return Some(SymbolStrExpr::Expr {
1108                     item,
1109                     is_ident,
1110                     is_to_owned,
1111                 });
1112             }
1113         }
1114         // is a string constant
1115         if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) {
1116             let value = Symbol::intern(&s).as_u32();
1117             // ...which matches a symbol constant
1118             if let Some(&def_id) = self.symbol_map.get(&value) {
1119                 return Some(SymbolStrExpr::Const(def_id));
1120             }
1121         }
1122         None
1123     }
1124 }
1125
1126 enum SymbolStrExpr<'tcx> {
1127     /// a string constant with a corresponding symbol constant
1128     Const(DefId),
1129     /// a "symbol to string" expression like `symbol.as_str()`
1130     Expr {
1131         /// part that evaluates to `Symbol` or `Ident`
1132         item: &'tcx Expr<'tcx>,
1133         is_ident: bool,
1134         /// whether an owned `String` is created like `to_ident_string()`
1135         is_to_owned: bool,
1136     },
1137 }
1138
1139 impl<'tcx> SymbolStrExpr<'tcx> {
1140     /// Returns a snippet that evaluates to a `Symbol` and is const if possible
1141     fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> {
1142         match *self {
1143             Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(),
1144             Self::Expr { item, is_ident, .. } => {
1145                 let mut snip = snippet(cx, item.span.source_callsite(), "..");
1146                 if is_ident {
1147                     // get `Ident.name`
1148                     snip.to_mut().push_str(".name");
1149                 }
1150                 snip
1151             },
1152         }
1153     }
1154 }
1155
1156 declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]);
1157
1158 impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
1159     fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
1160         let (local, after, if_chain_span) = if_chain! {
1161             if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts;
1162             if let Some(if_chain_span) = is_expn_of(block.span, "if_chain");
1163             then { (local, after, if_chain_span) } else { return }
1164         };
1165         if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) {
1166             span_lint(
1167                 cx,
1168                 IF_CHAIN_STYLE,
1169                 if_chain_local_span(cx, local, if_chain_span),
1170                 "`let` expression should be above the `if_chain!`",
1171             );
1172         } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) {
1173             span_lint(
1174                 cx,
1175                 IF_CHAIN_STYLE,
1176                 if_chain_local_span(cx, local, if_chain_span),
1177                 "`let` expression should be inside `then { .. }`",
1178             );
1179         }
1180     }
1181
1182     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
1183         let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) {
1184             (cond, then, r#else.is_some())
1185         } else {
1186             return;
1187         };
1188         let then_block = match then.kind {
1189             ExprKind::Block(block, _) => block,
1190             _ => return,
1191         };
1192         let if_chain_span = is_expn_of(expr.span, "if_chain");
1193         if !els {
1194             check_nested_if_chains(cx, expr, then_block, if_chain_span);
1195         }
1196         let if_chain_span = match if_chain_span {
1197             None => return,
1198             Some(span) => span,
1199         };
1200         // check for `if a && b;`
1201         if_chain! {
1202             if let ExprKind::Binary(op, _, _) = cond.kind;
1203             if op.node == BinOpKind::And;
1204             if cx.sess().source_map().is_multiline(cond.span);
1205             then {
1206                 span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`");
1207             }
1208         }
1209         if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span)
1210             && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span)
1211         {
1212             span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`");
1213         }
1214     }
1215 }
1216
1217 fn check_nested_if_chains(
1218     cx: &LateContext<'_>,
1219     if_expr: &Expr<'_>,
1220     then_block: &Block<'_>,
1221     if_chain_span: Option<Span>,
1222 ) {
1223     #[rustfmt::skip]
1224     let (head, tail) = match *then_block {
1225         Block { stmts, expr: Some(tail), .. } => (stmts, tail),
1226         Block {
1227             stmts: &[
1228                 ref head @ ..,
1229                 Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. }
1230             ],
1231             ..
1232         } => (head, tail),
1233         _ => return,
1234     };
1235     if_chain! {
1236         if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail);
1237         let sm = cx.sess().source_map();
1238         if head
1239             .iter()
1240             .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span));
1241         if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr);
1242         then {} else { return }
1243     }
1244     let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) {
1245         (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"),
1246         (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"),
1247         (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"),
1248         _ => return,
1249     };
1250     span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| {
1251         let (span, msg) = match head {
1252             [] => return,
1253             [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"),
1254             [a, .., b] => (
1255                 a.span.to(b.span),
1256                 "these `let` statements can also be in the `if_chain!`",
1257             ),
1258         };
1259         diag.span_help(span, msg);
1260     });
1261 }
1262
1263 fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool {
1264     cx.tcx
1265         .hir()
1266         .parent_iter(hir_id)
1267         .find(|(_, node)| {
1268             #[rustfmt::skip]
1269             !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_))
1270         })
1271         .map_or(false, |(id, _)| {
1272             is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span)
1273         })
1274 }
1275
1276 /// Checks a trailing slice of statements and expression of a `Block` to see if they are part
1277 /// of the `then {..}` portion of an `if_chain!`
1278 fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool {
1279     let span = if let [stmt, ..] = stmts {
1280         stmt.span
1281     } else if let Some(expr) = expr {
1282         expr.span
1283     } else {
1284         // empty `then {}`
1285         return true;
1286     };
1287     is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span)
1288 }
1289
1290 /// Creates a `Span` for `let x = ..;` in an `if_chain!` call.
1291 fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span {
1292     let mut span = local.pat.span;
1293     if let Some(init) = local.init {
1294         span = span.to(init.span);
1295     }
1296     span.adjust(if_chain_span.ctxt().outer_expn());
1297     let sm = cx.sess().source_map();
1298     let span = sm.span_extend_to_prev_str(span, "let", false);
1299     let span = sm.span_extend_to_next_char(span, ';', false);
1300     Span::new(
1301         span.lo() - BytePos(3),
1302         span.hi() + BytePos(1),
1303         span.ctxt(),
1304         span.parent(),
1305     )
1306 }