]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/utils/internal_lints.rs
Auto merge of #9546 - kraktus:default_not_default_trait, r=xFrednet
[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_typeck::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 `{lint_name}` is not added to any `LintPass`"),
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({}, {}, {}, {}, {}, {help})",
858             and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span,
859         ),
860         Applicability::MachineApplicable,
861     );
862 }
863
864 fn suggest_note(
865     cx: &LateContext<'_>,
866     expr: &Expr<'_>,
867     and_then_snippets: &AndThenSnippets<'_>,
868     note: &str,
869     with_span: bool,
870 ) {
871     let note_span = if with_span {
872         format!("Some({})", and_then_snippets.span)
873     } else {
874         "None".to_string()
875     };
876
877     span_lint_and_sugg(
878         cx,
879         COLLAPSIBLE_SPAN_LINT_CALLS,
880         expr.span,
881         "this call is collapsible",
882         "collapse into",
883         format!(
884             "span_lint_and_note({}, {}, {}, {}, {note_span}, {note})",
885             and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg,
886         ),
887         Applicability::MachineApplicable,
888     );
889 }
890
891 declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]);
892
893 impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
894     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
895         if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) {
896             return;
897         }
898
899         if_chain! {
900             // Check if this is a call to utils::match_type()
901             if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
902             if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
903             // Extract the path to the matched type
904             if let Some(segments) = path_to_matched_type(cx, ty_path);
905             let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect();
906             if let Some(ty_did) = def_path_res(cx, &segments[..]).opt_def_id();
907             // Check if the matched type is a diagnostic item
908             if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did);
909             then {
910                 // TODO: check paths constants from external crates.
911                 let cx_snippet = snippet(cx, context.span, "_");
912                 let ty_snippet = snippet(cx, ty.span, "_");
913
914                 span_lint_and_sugg(
915                     cx,
916                     MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
917                     expr.span,
918                     "usage of `clippy_utils::ty::match_type()` on a type diagnostic item",
919                     "try",
920                     format!("clippy_utils::ty::is_type_diagnostic_item({cx_snippet}, {ty_snippet}, sym::{item_name})"),
921                     Applicability::MaybeIncorrect,
922                 );
923             }
924         }
925     }
926 }
927
928 fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<Symbol>> {
929     use rustc_hir::ItemKind;
930
931     match &expr.kind {
932         ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
933         ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) {
934             Res::Local(hir_id) => {
935                 let parent_id = cx.tcx.hir().get_parent_node(hir_id);
936                 if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) {
937                     if let Some(init) = local.init {
938                         return path_to_matched_type(cx, init);
939                     }
940                 }
941             },
942             Res::Def(DefKind::Const | DefKind::Static(..), def_id) => {
943                 if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
944                     if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
945                         let body = cx.tcx.hir().body(body_id);
946                         return path_to_matched_type(cx, body.value);
947                     }
948                 }
949             },
950             _ => {},
951         },
952         ExprKind::Array(exprs) => {
953             let segments: Vec<Symbol> = exprs
954                 .iter()
955                 .filter_map(|expr| {
956                     if let ExprKind::Lit(lit) = &expr.kind {
957                         if let LitKind::Str(sym, _) = lit.node {
958                             return Some(sym);
959                         }
960                     }
961
962                     None
963                 })
964                 .collect();
965
966             if segments.len() == exprs.len() {
967                 return Some(segments);
968             }
969         },
970         _ => {},
971     }
972
973     None
974 }
975
976 // This is not a complete resolver for paths. It works on all the paths currently used in the paths
977 // module.  That's all it does and all it needs to do.
978 pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
979     if def_path_res(cx, path) != Res::Err {
980         return true;
981     }
982
983     // Some implementations can't be found by `path_to_res`, particularly inherent
984     // implementations of native types. Check lang items.
985     let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
986     let lang_items = cx.tcx.lang_items();
987     // This list isn't complete, but good enough for our current list of paths.
988     let incoherent_impls = [
989         SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32),
990         SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64),
991         SimplifiedTypeGen::SliceSimplifiedType,
992         SimplifiedTypeGen::StrSimplifiedType,
993     ]
994     .iter()
995     .flat_map(|&ty| cx.tcx.incoherent_impls(ty));
996     for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) {
997         let lang_item_path = cx.get_def_path(*item_def_id);
998         if path_syms.starts_with(&lang_item_path) {
999             if let [item] = &path_syms[lang_item_path.len()..] {
1000                 if matches!(
1001                     cx.tcx.def_kind(*item_def_id),
1002                     DefKind::Mod | DefKind::Enum | DefKind::Trait
1003                 ) {
1004                     for child in cx.tcx.module_children(*item_def_id) {
1005                         if child.ident.name == *item {
1006                             return true;
1007                         }
1008                     }
1009                 } else {
1010                     for child in cx.tcx.associated_item_def_ids(*item_def_id) {
1011                         if cx.tcx.item_name(*child) == *item {
1012                             return true;
1013                         }
1014                     }
1015                 }
1016             }
1017         }
1018     }
1019
1020     false
1021 }
1022
1023 declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
1024
1025 impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
1026     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
1027         let local_def_id = &cx.tcx.parent_module(item.hir_id());
1028         let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
1029         if_chain! {
1030             if mod_name.as_str() == "paths";
1031             if let hir::ItemKind::Const(ty, body_id) = item.kind;
1032             let ty = hir_ty_to_ty(cx.tcx, ty);
1033             if let ty::Array(el_ty, _) = &ty.kind();
1034             if let ty::Ref(_, el_ty, _) = &el_ty.kind();
1035             if el_ty.is_str();
1036             let body = cx.tcx.hir().body(body_id);
1037             let typeck_results = cx.tcx.typeck_body(body_id);
1038             if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value);
1039             let path: Vec<&str> = path.iter().map(|x| {
1040                     if let Constant::Str(s) = x {
1041                         s.as_str()
1042                     } else {
1043                         // We checked the type of the constant above
1044                         unreachable!()
1045                     }
1046                 }).collect();
1047             if !check_path(cx, &path[..]);
1048             then {
1049                 span_lint(cx, INVALID_PATHS, item.span, "invalid path");
1050             }
1051         }
1052     }
1053 }
1054
1055 #[derive(Default)]
1056 pub struct InterningDefinedSymbol {
1057     // Maps the symbol value to the constant DefId.
1058     symbol_map: FxHashMap<u32, DefId>,
1059 }
1060
1061 impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]);
1062
1063 impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
1064     fn check_crate(&mut self, cx: &LateContext<'_>) {
1065         if !self.symbol_map.is_empty() {
1066             return;
1067         }
1068
1069         for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
1070             if let Some(def_id) = def_path_res(cx, module).opt_def_id() {
1071                 for item in cx.tcx.module_children(def_id).iter() {
1072                     if_chain! {
1073                         if let Res::Def(DefKind::Const, item_def_id) = item.res;
1074                         let ty = cx.tcx.type_of(item_def_id);
1075                         if match_type(cx, ty, &paths::SYMBOL);
1076                         if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
1077                         if let Ok(value) = value.to_u32();
1078                         then {
1079                             self.symbol_map.insert(value, item_def_id);
1080                         }
1081                     }
1082                 }
1083             }
1084         }
1085     }
1086
1087     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1088         if_chain! {
1089             if let ExprKind::Call(func, [arg]) = &expr.kind;
1090             if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
1091             if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
1092             if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
1093             let value = Symbol::intern(&arg).as_u32();
1094             if let Some(&def_id) = self.symbol_map.get(&value);
1095             then {
1096                 span_lint_and_sugg(
1097                     cx,
1098                     INTERNING_DEFINED_SYMBOL,
1099                     is_expn_of(expr.span, "sym").unwrap_or(expr.span),
1100                     "interning a defined symbol",
1101                     "try",
1102                     cx.tcx.def_path_str(def_id),
1103                     Applicability::MachineApplicable,
1104                 );
1105             }
1106         }
1107         if let ExprKind::Binary(op, left, right) = expr.kind {
1108             if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) {
1109                 let data = [
1110                     (left, self.symbol_str_expr(left, cx)),
1111                     (right, self.symbol_str_expr(right, cx)),
1112                 ];
1113                 match data {
1114                     // both operands are a symbol string
1115                     [(_, Some(left)), (_, Some(right))] => {
1116                         span_lint_and_sugg(
1117                             cx,
1118                             UNNECESSARY_SYMBOL_STR,
1119                             expr.span,
1120                             "unnecessary `Symbol` to string conversion",
1121                             "try",
1122                             format!(
1123                                 "{} {} {}",
1124                                 left.as_symbol_snippet(cx),
1125                                 op.node.as_str(),
1126                                 right.as_symbol_snippet(cx),
1127                             ),
1128                             Applicability::MachineApplicable,
1129                         );
1130                     },
1131                     // one of the operands is a symbol string
1132                     [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => {
1133                         // creating an owned string for comparison
1134                         if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) {
1135                             span_lint_and_sugg(
1136                                 cx,
1137                                 UNNECESSARY_SYMBOL_STR,
1138                                 expr.span,
1139                                 "unnecessary string allocation",
1140                                 "try",
1141                                 format!("{}.as_str()", symbol.as_symbol_snippet(cx)),
1142                                 Applicability::MachineApplicable,
1143                             );
1144                         }
1145                     },
1146                     // nothing found
1147                     [(_, None), (_, None)] => {},
1148                 }
1149             }
1150         }
1151     }
1152 }
1153
1154 impl InterningDefinedSymbol {
1155     fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> {
1156         static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD];
1157         static SYMBOL_STR_PATHS: &[&[&str]] = &[
1158             &paths::SYMBOL_AS_STR,
1159             &paths::SYMBOL_TO_IDENT_STRING,
1160             &paths::TO_STRING_METHOD,
1161         ];
1162         let call = if_chain! {
1163             if let ExprKind::AddrOf(_, _, e) = expr.kind;
1164             if let ExprKind::Unary(UnOp::Deref, e) = e.kind;
1165             then { e } else { expr }
1166         };
1167         if_chain! {
1168             // is a method call
1169             if let ExprKind::MethodCall(_, item, [], _) = call.kind;
1170             if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
1171             let ty = cx.typeck_results().expr_ty(item);
1172             // ...on either an Ident or a Symbol
1173             if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) {
1174                 Some(false)
1175             } else if match_type(cx, ty, &paths::IDENT) {
1176                 Some(true)
1177             } else {
1178                 None
1179             };
1180             // ...which converts it to a string
1181             let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS };
1182             if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path));
1183             then {
1184                 let is_to_owned = path.last().unwrap().ends_with("string");
1185                 return Some(SymbolStrExpr::Expr {
1186                     item,
1187                     is_ident,
1188                     is_to_owned,
1189                 });
1190             }
1191         }
1192         // is a string constant
1193         if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) {
1194             let value = Symbol::intern(&s).as_u32();
1195             // ...which matches a symbol constant
1196             if let Some(&def_id) = self.symbol_map.get(&value) {
1197                 return Some(SymbolStrExpr::Const(def_id));
1198             }
1199         }
1200         None
1201     }
1202 }
1203
1204 enum SymbolStrExpr<'tcx> {
1205     /// a string constant with a corresponding symbol constant
1206     Const(DefId),
1207     /// a "symbol to string" expression like `symbol.as_str()`
1208     Expr {
1209         /// part that evaluates to `Symbol` or `Ident`
1210         item: &'tcx Expr<'tcx>,
1211         is_ident: bool,
1212         /// whether an owned `String` is created like `to_ident_string()`
1213         is_to_owned: bool,
1214     },
1215 }
1216
1217 impl<'tcx> SymbolStrExpr<'tcx> {
1218     /// Returns a snippet that evaluates to a `Symbol` and is const if possible
1219     fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> {
1220         match *self {
1221             Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(),
1222             Self::Expr { item, is_ident, .. } => {
1223                 let mut snip = snippet(cx, item.span.source_callsite(), "..");
1224                 if is_ident {
1225                     // get `Ident.name`
1226                     snip.to_mut().push_str(".name");
1227                 }
1228                 snip
1229             },
1230         }
1231     }
1232 }
1233
1234 declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]);
1235
1236 impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
1237     fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
1238         let (local, after, if_chain_span) = if_chain! {
1239             if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts;
1240             if let Some(if_chain_span) = is_expn_of(block.span, "if_chain");
1241             then { (local, after, if_chain_span) } else { return }
1242         };
1243         if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) {
1244             span_lint(
1245                 cx,
1246                 IF_CHAIN_STYLE,
1247                 if_chain_local_span(cx, local, if_chain_span),
1248                 "`let` expression should be above the `if_chain!`",
1249             );
1250         } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) {
1251             span_lint(
1252                 cx,
1253                 IF_CHAIN_STYLE,
1254                 if_chain_local_span(cx, local, if_chain_span),
1255                 "`let` expression should be inside `then { .. }`",
1256             );
1257         }
1258     }
1259
1260     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
1261         let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) {
1262             (cond, then, r#else.is_some())
1263         } else {
1264             return;
1265         };
1266         let then_block = match then.kind {
1267             ExprKind::Block(block, _) => block,
1268             _ => return,
1269         };
1270         let if_chain_span = is_expn_of(expr.span, "if_chain");
1271         if !els {
1272             check_nested_if_chains(cx, expr, then_block, if_chain_span);
1273         }
1274         let if_chain_span = match if_chain_span {
1275             None => return,
1276             Some(span) => span,
1277         };
1278         // check for `if a && b;`
1279         if_chain! {
1280             if let ExprKind::Binary(op, _, _) = cond.kind;
1281             if op.node == BinOpKind::And;
1282             if cx.sess().source_map().is_multiline(cond.span);
1283             then {
1284                 span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`");
1285             }
1286         }
1287         if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span)
1288             && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span)
1289         {
1290             span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`");
1291         }
1292     }
1293 }
1294
1295 fn check_nested_if_chains(
1296     cx: &LateContext<'_>,
1297     if_expr: &Expr<'_>,
1298     then_block: &Block<'_>,
1299     if_chain_span: Option<Span>,
1300 ) {
1301     #[rustfmt::skip]
1302     let (head, tail) = match *then_block {
1303         Block { stmts, expr: Some(tail), .. } => (stmts, tail),
1304         Block {
1305             stmts: &[
1306                 ref head @ ..,
1307                 Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. }
1308             ],
1309             ..
1310         } => (head, tail),
1311         _ => return,
1312     };
1313     if_chain! {
1314         if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail);
1315         let sm = cx.sess().source_map();
1316         if head
1317             .iter()
1318             .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span));
1319         if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr);
1320         then {} else { return }
1321     }
1322     let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) {
1323         (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"),
1324         (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"),
1325         (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"),
1326         _ => return,
1327     };
1328     span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| {
1329         let (span, msg) = match head {
1330             [] => return,
1331             [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"),
1332             [a, .., b] => (
1333                 a.span.to(b.span),
1334                 "these `let` statements can also be in the `if_chain!`",
1335             ),
1336         };
1337         diag.span_help(span, msg);
1338     });
1339 }
1340
1341 fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool {
1342     cx.tcx
1343         .hir()
1344         .parent_iter(hir_id)
1345         .find(|(_, node)| {
1346             #[rustfmt::skip]
1347             !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_))
1348         })
1349         .map_or(false, |(id, _)| {
1350             is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span)
1351         })
1352 }
1353
1354 /// Checks a trailing slice of statements and expression of a `Block` to see if they are part
1355 /// of the `then {..}` portion of an `if_chain!`
1356 fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool {
1357     let span = if let [stmt, ..] = stmts {
1358         stmt.span
1359     } else if let Some(expr) = expr {
1360         expr.span
1361     } else {
1362         // empty `then {}`
1363         return true;
1364     };
1365     is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span)
1366 }
1367
1368 /// Creates a `Span` for `let x = ..;` in an `if_chain!` call.
1369 fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span {
1370     let mut span = local.pat.span;
1371     if let Some(init) = local.init {
1372         span = span.to(init.span);
1373     }
1374     span.adjust(if_chain_span.ctxt().outer_expn());
1375     let sm = cx.sess().source_map();
1376     let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span);
1377     let span = sm.span_extend_to_next_char(span, ';', false);
1378     Span::new(
1379         span.lo() - BytePos(3),
1380         span.hi() + BytePos(1),
1381         span.ctxt(),
1382         span.parent(),
1383     )
1384 }
1385
1386 declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]);
1387
1388 impl LateLintPass<'_> for MsrvAttrImpl {
1389     fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
1390         if_chain! {
1391             if let hir::ItemKind::Impl(hir::Impl {
1392                 of_trait: Some(lint_pass_trait_ref),
1393                 self_ty,
1394                 items,
1395                 ..
1396             }) = &item.kind;
1397             if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id();
1398             let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS);
1399             if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS);
1400             let self_ty = hir_ty_to_ty(cx.tcx, self_ty);
1401             if let ty::Adt(self_ty_def, _) = self_ty.kind();
1402             if self_ty_def.is_struct();
1403             if self_ty_def.all_fields().any(|f| {
1404                 cx.tcx
1405                     .type_of(f.did)
1406                     .walk()
1407                     .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
1408                     .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION))
1409             });
1410             if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
1411             then {
1412                 let context = if is_late_pass { "LateContext" } else { "EarlyContext" };
1413                 let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" };
1414                 let span = cx.sess().source_map().span_through_char(item.span, '{');
1415                 span_lint_and_sugg(
1416                     cx,
1417                     MISSING_MSRV_ATTR_IMPL,
1418                     span,
1419                     &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"),
1420                     &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"),
1421                     format!("{}\n    extract_msrv_attr!({context});", snippet(cx, span, "..")),
1422                     Applicability::MachineApplicable,
1423                 );
1424             }
1425         }
1426     }
1427 }