]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
Merge commit 'e329249b6a3a98830d860c74c8234a8dd9407436' into clippyup
[rust.git] / src / tools / clippy / 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::macros::root_macro_call_first_node;
4 use clippy_utils::source::snippet;
5 use clippy_utils::ty::match_type;
6 use clippy_utils::{
7     def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path,
8     method_calls, paths, peel_blocks_with_stmt, 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::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::nested_filter;
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;
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 = "internal")]
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<&str> = None;
349                         for item in items {
350                             let name = item.ident.as_str();
351                             if let Some(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 let Some(macro_call) = root_macro_call_first_node(cx, item) {
415             if !matches!(
416                 &*cx.tcx.item_name(macro_call.def_id).as_str(),
417                 "impl_lint_pass" | "declare_lint_pass"
418             ) {
419                 return;
420             }
421             if let hir::ItemKind::Impl(hir::Impl {
422                 of_trait: None,
423                 items: impl_item_refs,
424                 ..
425             }) = item.kind
426             {
427                 let mut collector = LintCollector {
428                     output: &mut self.registered_lints,
429                     cx,
430                 };
431                 let body_id = cx.tcx.hir().body_owned_by(
432                     impl_item_refs
433                         .iter()
434                         .find(|iiref| iiref.ident.as_str() == "get_lints")
435                         .expect("LintPass needs to implement get_lints")
436                         .id
437                         .hir_id(),
438                 );
439                 collector.visit_expr(&cx.tcx.hir().body(body_id).value);
440             }
441         }
442     }
443
444     fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
445         if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
446             return;
447         }
448
449         for (lint_name, &lint_span) in &self.declared_lints {
450             // When using the `declare_tool_lint!` macro, the original `lint_span`'s
451             // file points to "<rustc macros>".
452             // `compiletest-rs` thinks that's an error in a different file and
453             // just ignores it. This causes the test in compile-fail/lint_pass
454             // not able to capture the error.
455             // Therefore, we need to climb the macro expansion tree and find the
456             // actual span that invoked `declare_tool_lint!`:
457             let lint_span = lint_span.ctxt().outer_expn_data().call_site;
458
459             if !self.registered_lints.contains(lint_name) {
460                 span_lint(
461                     cx,
462                     LINT_WITHOUT_LINT_PASS,
463                     lint_span,
464                     &format!("the lint `{}` is not added to any `LintPass`", lint_name),
465                 );
466             }
467         }
468     }
469 }
470
471 fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool {
472     if let TyKind::Rptr(
473         _,
474         MutTy {
475             ty: inner,
476             mutbl: Mutability::Not,
477         },
478     ) = ty.kind
479     {
480         if let TyKind::Path(ref path) = inner.kind {
481             if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
482                 return match_def_path(cx, def_id, &paths::LINT);
483             }
484         }
485     }
486
487     false
488 }
489
490 fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
491     if let Some(value) = extract_clippy_version_value(cx, item) {
492         // The `sym!` macro doesn't work as it only expects a single token.
493         // It's better to keep it this way and have a direct `Symbol::intern` call here.
494         if value == Symbol::intern("pre 1.29.0") {
495             return;
496         }
497
498         if RustcVersion::parse(&*value.as_str()).is_err() {
499             span_lint_and_help(
500                 cx,
501                 INVALID_CLIPPY_VERSION_ATTRIBUTE,
502                 item.span,
503                 "this item has an invalid `clippy::version` attribute",
504                 None,
505                 "please use a valid sematic version, see `doc/adding_lints.md`",
506             );
507         }
508     } else {
509         span_lint_and_help(
510             cx,
511             MISSING_CLIPPY_VERSION_ATTRIBUTE,
512             item.span,
513             "this lint is missing the `clippy::version` attribute or version value",
514             None,
515             "please use a `clippy::version` attribute, see `doc/adding_lints.md`",
516         );
517     }
518 }
519
520 /// This function extracts the version value of a `clippy::version` attribute if the given value has
521 /// one
522 fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
523     let attrs = cx.tcx.hir().attrs(item.hir_id());
524     attrs.iter().find_map(|attr| {
525         if_chain! {
526             // Identify attribute
527             if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind;
528             if let [tool_name, attr_name] = &attr_kind.path.segments[..];
529             if tool_name.ident.name == sym::clippy;
530             if attr_name.ident.name == sym::version;
531             if let Some(version) = attr.value_str();
532             then {
533                 Some(version)
534             } else {
535                 None
536             }
537         }
538     })
539 }
540
541 struct LintCollector<'a, 'tcx> {
542     output: &'a mut FxHashSet<Symbol>,
543     cx: &'a LateContext<'tcx>,
544 }
545
546 impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
547     type NestedFilter = nested_filter::All;
548
549     fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
550         if path.segments.len() == 1 {
551             self.output.insert(path.segments[0].ident.name);
552         }
553     }
554
555     fn nested_visit_map(&mut self) -> Self::Map {
556         self.cx.tcx.hir()
557     }
558 }
559
560 #[derive(Clone, Default)]
561 pub struct CompilerLintFunctions {
562     map: FxHashMap<&'static str, &'static str>,
563 }
564
565 impl CompilerLintFunctions {
566     #[must_use]
567     pub fn new() -> Self {
568         let mut map = FxHashMap::default();
569         map.insert("span_lint", "utils::span_lint");
570         map.insert("struct_span_lint", "utils::span_lint");
571         map.insert("lint", "utils::span_lint");
572         map.insert("span_lint_note", "utils::span_lint_and_note");
573         map.insert("span_lint_help", "utils::span_lint_and_help");
574         Self { map }
575     }
576 }
577
578 impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
579
580 impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
581     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
582         if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
583             return;
584         }
585
586         if_chain! {
587             if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
588             let fn_name = path.ident;
589             if let Some(sugg) = self.map.get(&*fn_name.as_str());
590             let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
591             if match_type(cx, ty, &paths::EARLY_CONTEXT)
592                 || match_type(cx, ty, &paths::LATE_CONTEXT);
593             then {
594                 span_lint_and_help(
595                     cx,
596                     COMPILER_LINT_FUNCTIONS,
597                     path.ident.span,
598                     "usage of a compiler lint function",
599                     None,
600                     &format!("please use the Clippy variant of this function: `{}`", sugg),
601                 );
602             }
603         }
604     }
605 }
606
607 declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
608
609 impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
610     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
611         if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
612             return;
613         }
614
615         let (method_names, arg_lists, spans) = method_calls(expr, 2);
616         let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect();
617         if_chain! {
618             if let ["expn_data", "outer_expn"] = method_names.as_slice();
619             let args = arg_lists[1];
620             if args.len() == 1;
621             let self_arg = &args[0];
622             let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
623             if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
624             then {
625                 span_lint_and_sugg(
626                     cx,
627                     OUTER_EXPN_EXPN_DATA,
628                     spans[1].with_hi(expr.span.hi()),
629                     "usage of `outer_expn().expn_data()`",
630                     "try",
631                     "outer_expn_data()".to_string(),
632                     Applicability::MachineApplicable,
633                 );
634             }
635         }
636     }
637 }
638
639 declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
640
641 impl EarlyLintPass for ProduceIce {
642     fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
643         assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
644     }
645 }
646
647 fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
648     match fn_kind {
649         FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
650         FnKind::Closure(..) => false,
651     }
652 }
653
654 declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
655
656 impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
657     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
658         if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
659             return;
660         }
661
662         if_chain! {
663             if let ExprKind::Call(func, and_then_args) = expr.kind;
664             if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
665             if and_then_args.len() == 5;
666             if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
667             let body = cx.tcx.hir().body(*body_id);
668             let only_expr = peel_blocks_with_stmt(&body.value);
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(Symbol::as_str).collect();
847             if let Some(ty_did) = def_path_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<Symbol>> {
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<Symbol> = 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);
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 def_path_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                 if matches!(
933                     cx.tcx.def_kind(*item_def_id),
934                     DefKind::Mod | DefKind::Enum | DefKind::Trait
935                 ) {
936                     for child in cx.tcx.module_children(*item_def_id) {
937                         if child.ident.name == *item {
938                             return true;
939                         }
940                     }
941                 } else {
942                     for child in cx.tcx.associated_item_def_ids(*item_def_id) {
943                         if cx.tcx.item_name(*child) == *item {
944                             return true;
945                         }
946                     }
947                 }
948             }
949         }
950     }
951
952     false
953 }
954
955 declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
956
957 impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
958     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
959         let local_def_id = &cx.tcx.parent_module(item.hir_id());
960         let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
961         if_chain! {
962             if mod_name.as_str() == "paths";
963             if let hir::ItemKind::Const(ty, body_id) = item.kind;
964             let ty = hir_ty_to_ty(cx.tcx, ty);
965             if let ty::Array(el_ty, _) = &ty.kind();
966             if let ty::Ref(_, el_ty, _) = &el_ty.kind();
967             if el_ty.is_str();
968             let body = cx.tcx.hir().body(body_id);
969             let typeck_results = cx.tcx.typeck_body(body_id);
970             if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value);
971             let path: Vec<&str> = path.iter().map(|x| {
972                     if let Constant::Str(s) = x {
973                         s.as_str()
974                     } else {
975                         // We checked the type of the constant above
976                         unreachable!()
977                     }
978                 }).collect();
979             if !check_path(cx, &path[..]);
980             then {
981                 span_lint(cx, INVALID_PATHS, item.span, "invalid path");
982             }
983         }
984     }
985 }
986
987 #[derive(Default)]
988 pub struct InterningDefinedSymbol {
989     // Maps the symbol value to the constant DefId.
990     symbol_map: FxHashMap<u32, DefId>,
991 }
992
993 impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]);
994
995 impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
996     fn check_crate(&mut self, cx: &LateContext<'_>) {
997         if !self.symbol_map.is_empty() {
998             return;
999         }
1000
1001         for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
1002             if let Some(def_id) = def_path_res(cx, module).opt_def_id() {
1003                 for item in cx.tcx.module_children(def_id).iter() {
1004                     if_chain! {
1005                         if let Res::Def(DefKind::Const, item_def_id) = item.res;
1006                         let ty = cx.tcx.type_of(item_def_id);
1007                         if match_type(cx, ty, &paths::SYMBOL);
1008                         if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
1009                         if let Ok(value) = value.to_u32();
1010                         then {
1011                             self.symbol_map.insert(value, item_def_id);
1012                         }
1013                     }
1014                 }
1015             }
1016         }
1017     }
1018
1019     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1020         if_chain! {
1021             if let ExprKind::Call(func, [arg]) = &expr.kind;
1022             if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
1023             if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
1024             if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
1025             let value = Symbol::intern(&arg).as_u32();
1026             if let Some(&def_id) = self.symbol_map.get(&value);
1027             then {
1028                 span_lint_and_sugg(
1029                     cx,
1030                     INTERNING_DEFINED_SYMBOL,
1031                     is_expn_of(expr.span, "sym").unwrap_or(expr.span),
1032                     "interning a defined symbol",
1033                     "try",
1034                     cx.tcx.def_path_str(def_id),
1035                     Applicability::MachineApplicable,
1036                 );
1037             }
1038         }
1039         if let ExprKind::Binary(op, left, right) = expr.kind {
1040             if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) {
1041                 let data = [
1042                     (left, self.symbol_str_expr(left, cx)),
1043                     (right, self.symbol_str_expr(right, cx)),
1044                 ];
1045                 match data {
1046                     // both operands are a symbol string
1047                     [(_, Some(left)), (_, Some(right))] => {
1048                         span_lint_and_sugg(
1049                             cx,
1050                             UNNECESSARY_SYMBOL_STR,
1051                             expr.span,
1052                             "unnecessary `Symbol` to string conversion",
1053                             "try",
1054                             format!(
1055                                 "{} {} {}",
1056                                 left.as_symbol_snippet(cx),
1057                                 op.node.as_str(),
1058                                 right.as_symbol_snippet(cx),
1059                             ),
1060                             Applicability::MachineApplicable,
1061                         );
1062                     },
1063                     // one of the operands is a symbol string
1064                     [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => {
1065                         // creating an owned string for comparison
1066                         if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) {
1067                             span_lint_and_sugg(
1068                                 cx,
1069                                 UNNECESSARY_SYMBOL_STR,
1070                                 expr.span,
1071                                 "unnecessary string allocation",
1072                                 "try",
1073                                 format!("{}.as_str()", symbol.as_symbol_snippet(cx)),
1074                                 Applicability::MachineApplicable,
1075                             );
1076                         }
1077                     },
1078                     // nothing found
1079                     [(_, None), (_, None)] => {},
1080                 }
1081             }
1082         }
1083     }
1084 }
1085
1086 impl InterningDefinedSymbol {
1087     fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> {
1088         static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD];
1089         static SYMBOL_STR_PATHS: &[&[&str]] = &[
1090             &paths::SYMBOL_AS_STR,
1091             &paths::SYMBOL_TO_IDENT_STRING,
1092             &paths::TO_STRING_METHOD,
1093         ];
1094         let call = if_chain! {
1095             if let ExprKind::AddrOf(_, _, e) = expr.kind;
1096             if let ExprKind::Unary(UnOp::Deref, e) = e.kind;
1097             then { e } else { expr }
1098         };
1099         if_chain! {
1100             // is a method call
1101             if let ExprKind::MethodCall(_, [item], _) = call.kind;
1102             if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
1103             let ty = cx.typeck_results().expr_ty(item);
1104             // ...on either an Ident or a Symbol
1105             if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) {
1106                 Some(false)
1107             } else if match_type(cx, ty, &paths::IDENT) {
1108                 Some(true)
1109             } else {
1110                 None
1111             };
1112             // ...which converts it to a string
1113             let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS };
1114             if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path));
1115             then {
1116                 let is_to_owned = path.last().unwrap().ends_with("string");
1117                 return Some(SymbolStrExpr::Expr {
1118                     item,
1119                     is_ident,
1120                     is_to_owned,
1121                 });
1122             }
1123         }
1124         // is a string constant
1125         if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) {
1126             let value = Symbol::intern(&s).as_u32();
1127             // ...which matches a symbol constant
1128             if let Some(&def_id) = self.symbol_map.get(&value) {
1129                 return Some(SymbolStrExpr::Const(def_id));
1130             }
1131         }
1132         None
1133     }
1134 }
1135
1136 enum SymbolStrExpr<'tcx> {
1137     /// a string constant with a corresponding symbol constant
1138     Const(DefId),
1139     /// a "symbol to string" expression like `symbol.as_str()`
1140     Expr {
1141         /// part that evaluates to `Symbol` or `Ident`
1142         item: &'tcx Expr<'tcx>,
1143         is_ident: bool,
1144         /// whether an owned `String` is created like `to_ident_string()`
1145         is_to_owned: bool,
1146     },
1147 }
1148
1149 impl<'tcx> SymbolStrExpr<'tcx> {
1150     /// Returns a snippet that evaluates to a `Symbol` and is const if possible
1151     fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> {
1152         match *self {
1153             Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(),
1154             Self::Expr { item, is_ident, .. } => {
1155                 let mut snip = snippet(cx, item.span.source_callsite(), "..");
1156                 if is_ident {
1157                     // get `Ident.name`
1158                     snip.to_mut().push_str(".name");
1159                 }
1160                 snip
1161             },
1162         }
1163     }
1164 }
1165
1166 declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]);
1167
1168 impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
1169     fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
1170         let (local, after, if_chain_span) = if_chain! {
1171             if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts;
1172             if let Some(if_chain_span) = is_expn_of(block.span, "if_chain");
1173             then { (local, after, if_chain_span) } else { return }
1174         };
1175         if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) {
1176             span_lint(
1177                 cx,
1178                 IF_CHAIN_STYLE,
1179                 if_chain_local_span(cx, local, if_chain_span),
1180                 "`let` expression should be above the `if_chain!`",
1181             );
1182         } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) {
1183             span_lint(
1184                 cx,
1185                 IF_CHAIN_STYLE,
1186                 if_chain_local_span(cx, local, if_chain_span),
1187                 "`let` expression should be inside `then { .. }`",
1188             );
1189         }
1190     }
1191
1192     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
1193         let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) {
1194             (cond, then, r#else.is_some())
1195         } else {
1196             return;
1197         };
1198         let then_block = match then.kind {
1199             ExprKind::Block(block, _) => block,
1200             _ => return,
1201         };
1202         let if_chain_span = is_expn_of(expr.span, "if_chain");
1203         if !els {
1204             check_nested_if_chains(cx, expr, then_block, if_chain_span);
1205         }
1206         let if_chain_span = match if_chain_span {
1207             None => return,
1208             Some(span) => span,
1209         };
1210         // check for `if a && b;`
1211         if_chain! {
1212             if let ExprKind::Binary(op, _, _) = cond.kind;
1213             if op.node == BinOpKind::And;
1214             if cx.sess().source_map().is_multiline(cond.span);
1215             then {
1216                 span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`");
1217             }
1218         }
1219         if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span)
1220             && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span)
1221         {
1222             span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`");
1223         }
1224     }
1225 }
1226
1227 fn check_nested_if_chains(
1228     cx: &LateContext<'_>,
1229     if_expr: &Expr<'_>,
1230     then_block: &Block<'_>,
1231     if_chain_span: Option<Span>,
1232 ) {
1233     #[rustfmt::skip]
1234     let (head, tail) = match *then_block {
1235         Block { stmts, expr: Some(tail), .. } => (stmts, tail),
1236         Block {
1237             stmts: &[
1238                 ref head @ ..,
1239                 Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. }
1240             ],
1241             ..
1242         } => (head, tail),
1243         _ => return,
1244     };
1245     if_chain! {
1246         if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail);
1247         let sm = cx.sess().source_map();
1248         if head
1249             .iter()
1250             .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span));
1251         if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr);
1252         then {} else { return }
1253     }
1254     let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) {
1255         (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"),
1256         (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"),
1257         (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"),
1258         _ => return,
1259     };
1260     span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| {
1261         let (span, msg) = match head {
1262             [] => return,
1263             [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"),
1264             [a, .., b] => (
1265                 a.span.to(b.span),
1266                 "these `let` statements can also be in the `if_chain!`",
1267             ),
1268         };
1269         diag.span_help(span, msg);
1270     });
1271 }
1272
1273 fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool {
1274     cx.tcx
1275         .hir()
1276         .parent_iter(hir_id)
1277         .find(|(_, node)| {
1278             #[rustfmt::skip]
1279             !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_))
1280         })
1281         .map_or(false, |(id, _)| {
1282             is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span)
1283         })
1284 }
1285
1286 /// Checks a trailing slice of statements and expression of a `Block` to see if they are part
1287 /// of the `then {..}` portion of an `if_chain!`
1288 fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool {
1289     let span = if let [stmt, ..] = stmts {
1290         stmt.span
1291     } else if let Some(expr) = expr {
1292         expr.span
1293     } else {
1294         // empty `then {}`
1295         return true;
1296     };
1297     is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span)
1298 }
1299
1300 /// Creates a `Span` for `let x = ..;` in an `if_chain!` call.
1301 fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span {
1302     let mut span = local.pat.span;
1303     if let Some(init) = local.init {
1304         span = span.to(init.span);
1305     }
1306     span.adjust(if_chain_span.ctxt().outer_expn());
1307     let sm = cx.sess().source_map();
1308     let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span);
1309     let span = sm.span_extend_to_next_char(span, ';', false);
1310     Span::new(
1311         span.lo() - BytePos(3),
1312         span.hi() + BytePos(1),
1313         span.ctxt(),
1314         span.parent(),
1315     )
1316 }