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