]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/utils/internal_lints.rs
25d74b8c49939da8a643e60a2da1e4b264440d91
[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::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::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy};
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_clippy_lint! {
341     /// ### What it does
342     /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV.
343     ///
344     pub MISSING_MSRV_ATTR_IMPL,
345     internal,
346     "checking if all necessary steps were taken when adding a MSRV to a lint"
347 }
348
349 declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
350
351 impl EarlyLintPass for ClippyLintsInternal {
352     fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
353         if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
354             if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
355                 if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
356                     if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
357                         let mut last_name: Option<&str> = None;
358                         for item in items {
359                             let name = item.ident.as_str();
360                             if let Some(last_name) = last_name {
361                                 if *last_name > *name {
362                                     span_lint(
363                                         cx,
364                                         CLIPPY_LINTS_INTERNAL,
365                                         item.span,
366                                         "this constant should be before the previous constant due to lexical \
367                                          ordering",
368                                     );
369                                 }
370                             }
371                             last_name = Some(name);
372                         }
373                     }
374                 }
375             }
376         }
377     }
378 }
379
380 #[derive(Clone, Debug, Default)]
381 pub struct LintWithoutLintPass {
382     declared_lints: FxHashMap<Symbol, Span>,
383     registered_lints: FxHashSet<Symbol>,
384 }
385
386 impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]);
387
388 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
389     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
390         if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) {
391             return;
392         }
393
394         if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
395             if is_lint_ref_type(cx, ty) {
396                 check_invalid_clippy_version_attribute(cx, item);
397
398                 let expr = &cx.tcx.hir().body(body_id).value;
399                 if_chain! {
400                     if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind;
401                     if let ExprKind::Struct(_, fields, _) = inner_exp.kind;
402                     let field = fields
403                         .iter()
404                         .find(|f| f.ident.as_str() == "desc")
405                         .expect("lints must have a description field");
406                     if let ExprKind::Lit(Spanned {
407                         node: LitKind::Str(ref sym, _),
408                         ..
409                     }) = field.expr.kind;
410                     if sym.as_str() == "default lint description";
411
412                     then {
413                         span_lint(
414                             cx,
415                             DEFAULT_LINT,
416                             item.span,
417                             &format!("the lint `{}` has the default lint description", item.ident.name),
418                         );
419                     }
420                 }
421                 self.declared_lints.insert(item.ident.name, item.span);
422             }
423         } else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
424             if !matches!(
425                 &*cx.tcx.item_name(macro_call.def_id).as_str(),
426                 "impl_lint_pass" | "declare_lint_pass"
427             ) {
428                 return;
429             }
430             if let hir::ItemKind::Impl(hir::Impl {
431                 of_trait: None,
432                 items: impl_item_refs,
433                 ..
434             }) = item.kind
435             {
436                 let mut collector = LintCollector {
437                     output: &mut self.registered_lints,
438                     cx,
439                 };
440                 let body_id = cx.tcx.hir().body_owned_by(
441                     impl_item_refs
442                         .iter()
443                         .find(|iiref| iiref.ident.as_str() == "get_lints")
444                         .expect("LintPass needs to implement get_lints")
445                         .id
446                         .hir_id(),
447                 );
448                 collector.visit_expr(&cx.tcx.hir().body(body_id).value);
449             }
450         }
451     }
452
453     fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
454         if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
455             return;
456         }
457
458         for (lint_name, &lint_span) in &self.declared_lints {
459             // When using the `declare_tool_lint!` macro, the original `lint_span`'s
460             // file points to "<rustc macros>".
461             // `compiletest-rs` thinks that's an error in a different file and
462             // just ignores it. This causes the test in compile-fail/lint_pass
463             // not able to capture the error.
464             // Therefore, we need to climb the macro expansion tree and find the
465             // actual span that invoked `declare_tool_lint!`:
466             let lint_span = lint_span.ctxt().outer_expn_data().call_site;
467
468             if !self.registered_lints.contains(lint_name) {
469                 span_lint(
470                     cx,
471                     LINT_WITHOUT_LINT_PASS,
472                     lint_span,
473                     &format!("the lint `{}` is not added to any `LintPass`", lint_name),
474                 );
475             }
476         }
477     }
478 }
479
480 fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool {
481     if let TyKind::Rptr(
482         _,
483         MutTy {
484             ty: inner,
485             mutbl: Mutability::Not,
486         },
487     ) = ty.kind
488     {
489         if let TyKind::Path(ref path) = inner.kind {
490             if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
491                 return match_def_path(cx, def_id, &paths::LINT);
492             }
493         }
494     }
495
496     false
497 }
498
499 fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
500     if let Some(value) = extract_clippy_version_value(cx, item) {
501         // The `sym!` macro doesn't work as it only expects a single token.
502         // It's better to keep it this way and have a direct `Symbol::intern` call here.
503         if value == Symbol::intern("pre 1.29.0") {
504             return;
505         }
506
507         if RustcVersion::parse(&*value.as_str()).is_err() {
508             span_lint_and_help(
509                 cx,
510                 INVALID_CLIPPY_VERSION_ATTRIBUTE,
511                 item.span,
512                 "this item has an invalid `clippy::version` attribute",
513                 None,
514                 "please use a valid sematic version, see `doc/adding_lints.md`",
515             );
516         }
517     } else {
518         span_lint_and_help(
519             cx,
520             MISSING_CLIPPY_VERSION_ATTRIBUTE,
521             item.span,
522             "this lint is missing the `clippy::version` attribute or version value",
523             None,
524             "please use a `clippy::version` attribute, see `doc/adding_lints.md`",
525         );
526     }
527 }
528
529 /// This function extracts the version value of a `clippy::version` attribute if the given value has
530 /// one
531 fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
532     let attrs = cx.tcx.hir().attrs(item.hir_id());
533     attrs.iter().find_map(|attr| {
534         if_chain! {
535             // Identify attribute
536             if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind;
537             if let [tool_name, attr_name] = &attr_kind.path.segments[..];
538             if tool_name.ident.name == sym::clippy;
539             if attr_name.ident.name == sym::version;
540             if let Some(version) = attr.value_str();
541             then {
542                 Some(version)
543             } else {
544                 None
545             }
546         }
547     })
548 }
549
550 struct LintCollector<'a, 'tcx> {
551     output: &'a mut FxHashSet<Symbol>,
552     cx: &'a LateContext<'tcx>,
553 }
554
555 impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
556     type NestedFilter = nested_filter::All;
557
558     fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
559         if path.segments.len() == 1 {
560             self.output.insert(path.segments[0].ident.name);
561         }
562     }
563
564     fn nested_visit_map(&mut self) -> Self::Map {
565         self.cx.tcx.hir()
566     }
567 }
568
569 #[derive(Clone, Default)]
570 pub struct CompilerLintFunctions {
571     map: FxHashMap<&'static str, &'static str>,
572 }
573
574 impl CompilerLintFunctions {
575     #[must_use]
576     pub fn new() -> Self {
577         let mut map = FxHashMap::default();
578         map.insert("span_lint", "utils::span_lint");
579         map.insert("struct_span_lint", "utils::span_lint");
580         map.insert("lint", "utils::span_lint");
581         map.insert("span_lint_note", "utils::span_lint_and_note");
582         map.insert("span_lint_help", "utils::span_lint_and_help");
583         Self { map }
584     }
585 }
586
587 impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
588
589 impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
590     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
591         if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
592             return;
593         }
594
595         if_chain! {
596             if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
597             let fn_name = path.ident;
598             if let Some(sugg) = self.map.get(&*fn_name.as_str());
599             let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
600             if match_type(cx, ty, &paths::EARLY_CONTEXT)
601                 || match_type(cx, ty, &paths::LATE_CONTEXT);
602             then {
603                 span_lint_and_help(
604                     cx,
605                     COMPILER_LINT_FUNCTIONS,
606                     path.ident.span,
607                     "usage of a compiler lint function",
608                     None,
609                     &format!("please use the Clippy variant of this function: `{}`", sugg),
610                 );
611             }
612         }
613     }
614 }
615
616 declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
617
618 impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
619     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
620         if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
621             return;
622         }
623
624         let (method_names, arg_lists, spans) = method_calls(expr, 2);
625         let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect();
626         if_chain! {
627             if let ["expn_data", "outer_expn"] = method_names.as_slice();
628             let args = arg_lists[1];
629             if args.len() == 1;
630             let self_arg = &args[0];
631             let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
632             if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
633             then {
634                 span_lint_and_sugg(
635                     cx,
636                     OUTER_EXPN_EXPN_DATA,
637                     spans[1].with_hi(expr.span.hi()),
638                     "usage of `outer_expn().expn_data()`",
639                     "try",
640                     "outer_expn_data()".to_string(),
641                     Applicability::MachineApplicable,
642                 );
643             }
644         }
645     }
646 }
647
648 declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
649
650 impl EarlyLintPass for ProduceIce {
651     fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
652         assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
653     }
654 }
655
656 fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
657     match fn_kind {
658         FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
659         FnKind::Closure(..) => false,
660     }
661 }
662
663 declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
664
665 impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
666     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
667         if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
668             return;
669         }
670
671         if_chain! {
672             if let ExprKind::Call(func, and_then_args) = expr.kind;
673             if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
674             if and_then_args.len() == 5;
675             if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
676             let body = cx.tcx.hir().body(*body_id);
677             let only_expr = peel_blocks_with_stmt(&body.value);
678             if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind;
679             then {
680                 let and_then_snippets = get_and_then_snippets(cx, and_then_args);
681                 let mut sle = SpanlessEq::new(cx).deny_side_effects();
682                 match &*ps.ident.as_str() {
683                     "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
684                         suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
685                     },
686                     "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
687                         let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
688                         suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
689                     },
690                     "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
691                         let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
692                         suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
693                     },
694                     "help" => {
695                         let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
696                         suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
697                     }
698                     "note" => {
699                         let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
700                         suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
701                     }
702                     _  => (),
703                 }
704             }
705         }
706     }
707 }
708
709 struct AndThenSnippets<'a> {
710     cx: Cow<'a, str>,
711     lint: Cow<'a, str>,
712     span: Cow<'a, str>,
713     msg: Cow<'a, str>,
714 }
715
716 fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> {
717     let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx");
718     let lint_snippet = snippet(cx, and_then_snippets[1].span, "..");
719     let span_snippet = snippet(cx, and_then_snippets[2].span, "span");
720     let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#);
721
722     AndThenSnippets {
723         cx: cx_snippet,
724         lint: lint_snippet,
725         span: span_snippet,
726         msg: msg_snippet,
727     }
728 }
729
730 struct SpanSuggestionSnippets<'a> {
731     help: Cow<'a, str>,
732     sugg: Cow<'a, str>,
733     applicability: Cow<'a, str>,
734 }
735
736 fn span_suggestion_snippets<'a, 'hir>(
737     cx: &LateContext<'_>,
738     span_call_args: &'hir [Expr<'hir>],
739 ) -> SpanSuggestionSnippets<'a> {
740     let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
741     let sugg_snippet = snippet(cx, span_call_args[3].span, "..");
742     let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable");
743
744     SpanSuggestionSnippets {
745         help: help_snippet,
746         sugg: sugg_snippet,
747         applicability: applicability_snippet,
748     }
749 }
750
751 fn suggest_suggestion(
752     cx: &LateContext<'_>,
753     expr: &Expr<'_>,
754     and_then_snippets: &AndThenSnippets<'_>,
755     span_suggestion_snippets: &SpanSuggestionSnippets<'_>,
756 ) {
757     span_lint_and_sugg(
758         cx,
759         COLLAPSIBLE_SPAN_LINT_CALLS,
760         expr.span,
761         "this call is collapsible",
762         "collapse into",
763         format!(
764             "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})",
765             and_then_snippets.cx,
766             and_then_snippets.lint,
767             and_then_snippets.span,
768             and_then_snippets.msg,
769             span_suggestion_snippets.help,
770             span_suggestion_snippets.sugg,
771             span_suggestion_snippets.applicability
772         ),
773         Applicability::MachineApplicable,
774     );
775 }
776
777 fn suggest_help(
778     cx: &LateContext<'_>,
779     expr: &Expr<'_>,
780     and_then_snippets: &AndThenSnippets<'_>,
781     help: &str,
782     with_span: bool,
783 ) {
784     let option_span = if with_span {
785         format!("Some({})", and_then_snippets.span)
786     } else {
787         "None".to_string()
788     };
789
790     span_lint_and_sugg(
791         cx,
792         COLLAPSIBLE_SPAN_LINT_CALLS,
793         expr.span,
794         "this call is collapsible",
795         "collapse into",
796         format!(
797             "span_lint_and_help({}, {}, {}, {}, {}, {})",
798             and_then_snippets.cx,
799             and_then_snippets.lint,
800             and_then_snippets.span,
801             and_then_snippets.msg,
802             &option_span,
803             help
804         ),
805         Applicability::MachineApplicable,
806     );
807 }
808
809 fn suggest_note(
810     cx: &LateContext<'_>,
811     expr: &Expr<'_>,
812     and_then_snippets: &AndThenSnippets<'_>,
813     note: &str,
814     with_span: bool,
815 ) {
816     let note_span = if with_span {
817         format!("Some({})", and_then_snippets.span)
818     } else {
819         "None".to_string()
820     };
821
822     span_lint_and_sugg(
823         cx,
824         COLLAPSIBLE_SPAN_LINT_CALLS,
825         expr.span,
826         "this call is collspible",
827         "collapse into",
828         format!(
829             "span_lint_and_note({}, {}, {}, {}, {}, {})",
830             and_then_snippets.cx,
831             and_then_snippets.lint,
832             and_then_snippets.span,
833             and_then_snippets.msg,
834             note_span,
835             note
836         ),
837         Applicability::MachineApplicable,
838     );
839 }
840
841 declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]);
842
843 impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
844     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
845         if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) {
846             return;
847         }
848
849         if_chain! {
850             // Check if this is a call to utils::match_type()
851             if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
852             if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
853             // Extract the path to the matched type
854             if let Some(segments) = path_to_matched_type(cx, ty_path);
855             let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect();
856             if let Some(ty_did) = def_path_res(cx, &segments[..]).opt_def_id();
857             // Check if the matched type is a diagnostic item
858             if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did);
859             then {
860                 // TODO: check paths constants from external crates.
861                 let cx_snippet = snippet(cx, context.span, "_");
862                 let ty_snippet = snippet(cx, ty.span, "_");
863
864                 span_lint_and_sugg(
865                     cx,
866                     MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
867                     expr.span,
868                     "usage of `clippy_utils::ty::match_type()` on a type diagnostic item",
869                     "try",
870                     format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
871                     Applicability::MaybeIncorrect,
872                 );
873             }
874         }
875     }
876 }
877
878 fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<Symbol>> {
879     use rustc_hir::ItemKind;
880
881     match &expr.kind {
882         ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
883         ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) {
884             Res::Local(hir_id) => {
885                 let parent_id = cx.tcx.hir().get_parent_node(hir_id);
886                 if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) {
887                     if let Some(init) = local.init {
888                         return path_to_matched_type(cx, init);
889                     }
890                 }
891             },
892             Res::Def(DefKind::Const | DefKind::Static(..), def_id) => {
893                 if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
894                     if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
895                         let body = cx.tcx.hir().body(body_id);
896                         return path_to_matched_type(cx, &body.value);
897                     }
898                 }
899             },
900             _ => {},
901         },
902         ExprKind::Array(exprs) => {
903             let segments: Vec<Symbol> = exprs
904                 .iter()
905                 .filter_map(|expr| {
906                     if let ExprKind::Lit(lit) = &expr.kind {
907                         if let LitKind::Str(sym, _) = lit.node {
908                             return Some(sym);
909                         }
910                     }
911
912                     None
913                 })
914                 .collect();
915
916             if segments.len() == exprs.len() {
917                 return Some(segments);
918             }
919         },
920         _ => {},
921     }
922
923     None
924 }
925
926 // This is not a complete resolver for paths. It works on all the paths currently used in the paths
927 // module.  That's all it does and all it needs to do.
928 pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
929     if def_path_res(cx, path) != Res::Err {
930         return true;
931     }
932
933     // Some implementations can't be found by `path_to_res`, particularly inherent
934     // implementations of native types. Check lang items.
935     let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
936     let lang_items = cx.tcx.lang_items();
937     // This list isn't complete, but good enough for our current list of paths.
938     let incoherent_impls = [
939         SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32),
940         SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64),
941         SimplifiedTypeGen::SliceSimplifiedType,
942         SimplifiedTypeGen::StrSimplifiedType,
943     ]
944     .iter()
945     .flat_map(|&ty| cx.tcx.incoherent_impls(ty));
946     for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) {
947         let lang_item_path = cx.get_def_path(*item_def_id);
948         if path_syms.starts_with(&lang_item_path) {
949             if let [item] = &path_syms[lang_item_path.len()..] {
950                 if matches!(
951                     cx.tcx.def_kind(*item_def_id),
952                     DefKind::Mod | DefKind::Enum | DefKind::Trait
953                 ) {
954                     for child in cx.tcx.module_children(*item_def_id) {
955                         if child.ident.name == *item {
956                             return true;
957                         }
958                     }
959                 } else {
960                     for child in cx.tcx.associated_item_def_ids(*item_def_id) {
961                         if cx.tcx.item_name(*child) == *item {
962                             return true;
963                         }
964                     }
965                 }
966             }
967         }
968     }
969
970     false
971 }
972
973 declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
974
975 impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
976     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
977         let local_def_id = &cx.tcx.parent_module(item.hir_id());
978         let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
979         if_chain! {
980             if mod_name.as_str() == "paths";
981             if let hir::ItemKind::Const(ty, body_id) = item.kind;
982             let ty = hir_ty_to_ty(cx.tcx, ty);
983             if let ty::Array(el_ty, _) = &ty.kind();
984             if let ty::Ref(_, el_ty, _) = &el_ty.kind();
985             if el_ty.is_str();
986             let body = cx.tcx.hir().body(body_id);
987             let typeck_results = cx.tcx.typeck_body(body_id);
988             if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value);
989             let path: Vec<&str> = path.iter().map(|x| {
990                     if let Constant::Str(s) = x {
991                         s.as_str()
992                     } else {
993                         // We checked the type of the constant above
994                         unreachable!()
995                     }
996                 }).collect();
997             if !check_path(cx, &path[..]);
998             then {
999                 span_lint(cx, INVALID_PATHS, item.span, "invalid path");
1000             }
1001         }
1002     }
1003 }
1004
1005 #[derive(Default)]
1006 pub struct InterningDefinedSymbol {
1007     // Maps the symbol value to the constant DefId.
1008     symbol_map: FxHashMap<u32, DefId>,
1009 }
1010
1011 impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]);
1012
1013 impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
1014     fn check_crate(&mut self, cx: &LateContext<'_>) {
1015         if !self.symbol_map.is_empty() {
1016             return;
1017         }
1018
1019         for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
1020             if let Some(def_id) = def_path_res(cx, module).opt_def_id() {
1021                 for item in cx.tcx.module_children(def_id).iter() {
1022                     if_chain! {
1023                         if let Res::Def(DefKind::Const, item_def_id) = item.res;
1024                         let ty = cx.tcx.type_of(item_def_id);
1025                         if match_type(cx, ty, &paths::SYMBOL);
1026                         if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
1027                         if let Ok(value) = value.to_u32();
1028                         then {
1029                             self.symbol_map.insert(value, item_def_id);
1030                         }
1031                     }
1032                 }
1033             }
1034         }
1035     }
1036
1037     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1038         if_chain! {
1039             if let ExprKind::Call(func, [arg]) = &expr.kind;
1040             if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
1041             if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
1042             if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
1043             let value = Symbol::intern(&arg).as_u32();
1044             if let Some(&def_id) = self.symbol_map.get(&value);
1045             then {
1046                 span_lint_and_sugg(
1047                     cx,
1048                     INTERNING_DEFINED_SYMBOL,
1049                     is_expn_of(expr.span, "sym").unwrap_or(expr.span),
1050                     "interning a defined symbol",
1051                     "try",
1052                     cx.tcx.def_path_str(def_id),
1053                     Applicability::MachineApplicable,
1054                 );
1055             }
1056         }
1057         if let ExprKind::Binary(op, left, right) = expr.kind {
1058             if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) {
1059                 let data = [
1060                     (left, self.symbol_str_expr(left, cx)),
1061                     (right, self.symbol_str_expr(right, cx)),
1062                 ];
1063                 match data {
1064                     // both operands are a symbol string
1065                     [(_, Some(left)), (_, Some(right))] => {
1066                         span_lint_and_sugg(
1067                             cx,
1068                             UNNECESSARY_SYMBOL_STR,
1069                             expr.span,
1070                             "unnecessary `Symbol` to string conversion",
1071                             "try",
1072                             format!(
1073                                 "{} {} {}",
1074                                 left.as_symbol_snippet(cx),
1075                                 op.node.as_str(),
1076                                 right.as_symbol_snippet(cx),
1077                             ),
1078                             Applicability::MachineApplicable,
1079                         );
1080                     },
1081                     // one of the operands is a symbol string
1082                     [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => {
1083                         // creating an owned string for comparison
1084                         if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) {
1085                             span_lint_and_sugg(
1086                                 cx,
1087                                 UNNECESSARY_SYMBOL_STR,
1088                                 expr.span,
1089                                 "unnecessary string allocation",
1090                                 "try",
1091                                 format!("{}.as_str()", symbol.as_symbol_snippet(cx)),
1092                                 Applicability::MachineApplicable,
1093                             );
1094                         }
1095                     },
1096                     // nothing found
1097                     [(_, None), (_, None)] => {},
1098                 }
1099             }
1100         }
1101     }
1102 }
1103
1104 impl InterningDefinedSymbol {
1105     fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> {
1106         static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD];
1107         static SYMBOL_STR_PATHS: &[&[&str]] = &[
1108             &paths::SYMBOL_AS_STR,
1109             &paths::SYMBOL_TO_IDENT_STRING,
1110             &paths::TO_STRING_METHOD,
1111         ];
1112         let call = if_chain! {
1113             if let ExprKind::AddrOf(_, _, e) = expr.kind;
1114             if let ExprKind::Unary(UnOp::Deref, e) = e.kind;
1115             then { e } else { expr }
1116         };
1117         if_chain! {
1118             // is a method call
1119             if let ExprKind::MethodCall(_, [item], _) = call.kind;
1120             if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
1121             let ty = cx.typeck_results().expr_ty(item);
1122             // ...on either an Ident or a Symbol
1123             if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) {
1124                 Some(false)
1125             } else if match_type(cx, ty, &paths::IDENT) {
1126                 Some(true)
1127             } else {
1128                 None
1129             };
1130             // ...which converts it to a string
1131             let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS };
1132             if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path));
1133             then {
1134                 let is_to_owned = path.last().unwrap().ends_with("string");
1135                 return Some(SymbolStrExpr::Expr {
1136                     item,
1137                     is_ident,
1138                     is_to_owned,
1139                 });
1140             }
1141         }
1142         // is a string constant
1143         if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) {
1144             let value = Symbol::intern(&s).as_u32();
1145             // ...which matches a symbol constant
1146             if let Some(&def_id) = self.symbol_map.get(&value) {
1147                 return Some(SymbolStrExpr::Const(def_id));
1148             }
1149         }
1150         None
1151     }
1152 }
1153
1154 enum SymbolStrExpr<'tcx> {
1155     /// a string constant with a corresponding symbol constant
1156     Const(DefId),
1157     /// a "symbol to string" expression like `symbol.as_str()`
1158     Expr {
1159         /// part that evaluates to `Symbol` or `Ident`
1160         item: &'tcx Expr<'tcx>,
1161         is_ident: bool,
1162         /// whether an owned `String` is created like `to_ident_string()`
1163         is_to_owned: bool,
1164     },
1165 }
1166
1167 impl<'tcx> SymbolStrExpr<'tcx> {
1168     /// Returns a snippet that evaluates to a `Symbol` and is const if possible
1169     fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> {
1170         match *self {
1171             Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(),
1172             Self::Expr { item, is_ident, .. } => {
1173                 let mut snip = snippet(cx, item.span.source_callsite(), "..");
1174                 if is_ident {
1175                     // get `Ident.name`
1176                     snip.to_mut().push_str(".name");
1177                 }
1178                 snip
1179             },
1180         }
1181     }
1182 }
1183
1184 declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]);
1185
1186 impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
1187     fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
1188         let (local, after, if_chain_span) = if_chain! {
1189             if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts;
1190             if let Some(if_chain_span) = is_expn_of(block.span, "if_chain");
1191             then { (local, after, if_chain_span) } else { return }
1192         };
1193         if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) {
1194             span_lint(
1195                 cx,
1196                 IF_CHAIN_STYLE,
1197                 if_chain_local_span(cx, local, if_chain_span),
1198                 "`let` expression should be above the `if_chain!`",
1199             );
1200         } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) {
1201             span_lint(
1202                 cx,
1203                 IF_CHAIN_STYLE,
1204                 if_chain_local_span(cx, local, if_chain_span),
1205                 "`let` expression should be inside `then { .. }`",
1206             );
1207         }
1208     }
1209
1210     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
1211         let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) {
1212             (cond, then, r#else.is_some())
1213         } else {
1214             return;
1215         };
1216         let then_block = match then.kind {
1217             ExprKind::Block(block, _) => block,
1218             _ => return,
1219         };
1220         let if_chain_span = is_expn_of(expr.span, "if_chain");
1221         if !els {
1222             check_nested_if_chains(cx, expr, then_block, if_chain_span);
1223         }
1224         let if_chain_span = match if_chain_span {
1225             None => return,
1226             Some(span) => span,
1227         };
1228         // check for `if a && b;`
1229         if_chain! {
1230             if let ExprKind::Binary(op, _, _) = cond.kind;
1231             if op.node == BinOpKind::And;
1232             if cx.sess().source_map().is_multiline(cond.span);
1233             then {
1234                 span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`");
1235             }
1236         }
1237         if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span)
1238             && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span)
1239         {
1240             span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`");
1241         }
1242     }
1243 }
1244
1245 fn check_nested_if_chains(
1246     cx: &LateContext<'_>,
1247     if_expr: &Expr<'_>,
1248     then_block: &Block<'_>,
1249     if_chain_span: Option<Span>,
1250 ) {
1251     #[rustfmt::skip]
1252     let (head, tail) = match *then_block {
1253         Block { stmts, expr: Some(tail), .. } => (stmts, tail),
1254         Block {
1255             stmts: &[
1256                 ref head @ ..,
1257                 Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. }
1258             ],
1259             ..
1260         } => (head, tail),
1261         _ => return,
1262     };
1263     if_chain! {
1264         if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail);
1265         let sm = cx.sess().source_map();
1266         if head
1267             .iter()
1268             .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span));
1269         if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr);
1270         then {} else { return }
1271     }
1272     let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) {
1273         (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"),
1274         (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"),
1275         (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"),
1276         _ => return,
1277     };
1278     span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| {
1279         let (span, msg) = match head {
1280             [] => return,
1281             [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"),
1282             [a, .., b] => (
1283                 a.span.to(b.span),
1284                 "these `let` statements can also be in the `if_chain!`",
1285             ),
1286         };
1287         diag.span_help(span, msg);
1288     });
1289 }
1290
1291 fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool {
1292     cx.tcx
1293         .hir()
1294         .parent_iter(hir_id)
1295         .find(|(_, node)| {
1296             #[rustfmt::skip]
1297             !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_))
1298         })
1299         .map_or(false, |(id, _)| {
1300             is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span)
1301         })
1302 }
1303
1304 /// Checks a trailing slice of statements and expression of a `Block` to see if they are part
1305 /// of the `then {..}` portion of an `if_chain!`
1306 fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool {
1307     let span = if let [stmt, ..] = stmts {
1308         stmt.span
1309     } else if let Some(expr) = expr {
1310         expr.span
1311     } else {
1312         // empty `then {}`
1313         return true;
1314     };
1315     is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span)
1316 }
1317
1318 /// Creates a `Span` for `let x = ..;` in an `if_chain!` call.
1319 fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span {
1320     let mut span = local.pat.span;
1321     if let Some(init) = local.init {
1322         span = span.to(init.span);
1323     }
1324     span.adjust(if_chain_span.ctxt().outer_expn());
1325     let sm = cx.sess().source_map();
1326     let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span);
1327     let span = sm.span_extend_to_next_char(span, ';', false);
1328     Span::new(
1329         span.lo() - BytePos(3),
1330         span.hi() + BytePos(1),
1331         span.ctxt(),
1332         span.parent(),
1333     )
1334 }
1335
1336 declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]);
1337
1338 impl LateLintPass<'_> for MsrvAttrImpl {
1339     fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
1340         if_chain! {
1341             if let hir::ItemKind::Impl(hir::Impl {
1342                 of_trait: Some(lint_pass_trait_ref),
1343                 self_ty,
1344                 items,
1345                 ..
1346             }) = &item.kind;
1347             if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id();
1348             let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS);
1349             if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS);
1350             let self_ty = hir_ty_to_ty(cx.tcx, self_ty);
1351             if let ty::Adt(self_ty_def, _) = self_ty.kind();
1352             if self_ty_def.is_struct();
1353             if self_ty_def.all_fields().any(|f| {
1354                 cx.tcx
1355                     .type_of(f.did)
1356                     .walk()
1357                     .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
1358                     .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION))
1359             });
1360             if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
1361             then {
1362                 let context = if is_late_pass { "LateContext" } else { "EarlyContext" };
1363                 let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" };
1364                 let span = cx.sess().source_map().span_through_char(item.span, '{');
1365                 span_lint_and_sugg(
1366                     cx,
1367                     MISSING_MSRV_ATTR_IMPL,
1368                     span,
1369                     &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"),
1370                     &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"),
1371                     format!("{}\n    extract_msrv_attr!({context});", snippet(cx, span, "..")),
1372                     Applicability::MachineApplicable,
1373                 );
1374             }
1375         }
1376     }
1377 }