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