]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/utils/internal_lints.rs
Change lint to use const sym
[rust.git] / clippy_lints / src / utils / internal_lints.rs
1 use crate::consts::{constant_simple, Constant};
2 use crate::utils::{
3     is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints,
4     snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
5 };
6 use if_chain::if_chain;
7 use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId};
8 use rustc_ast::visit::FnKind;
9 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
10 use rustc_errors::Applicability;
11 use rustc_hir as hir;
12 use rustc_hir::def::{DefKind, Res};
13 use rustc_hir::hir_id::CRATE_HIR_ID;
14 use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
15 use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind};
16 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
17 use rustc_middle::hir::map::Map;
18 use rustc_middle::ty;
19 use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
20 use rustc_span::source_map::{Span, Spanned};
21 use rustc_span::symbol::{Symbol, SymbolStr};
22 use rustc_typeck::hir_ty_to_ty;
23
24 use std::borrow::{Borrow, Cow};
25
26 declare_clippy_lint! {
27     /// **What it does:** Checks for various things we like to keep tidy in clippy.
28     ///
29     /// **Why is this bad?** We like to pretend we're an example of tidy code.
30     ///
31     /// **Known problems:** None.
32     ///
33     /// **Example:** Wrong ordering of the util::paths constants.
34     pub CLIPPY_LINTS_INTERNAL,
35     internal,
36     "various things that will negatively affect your clippy experience"
37 }
38
39 declare_clippy_lint! {
40     /// **What it does:** Ensures every lint is associated to a `LintPass`.
41     ///
42     /// **Why is this bad?** The compiler only knows lints via a `LintPass`. Without
43     /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
44     /// know the name of the lint.
45     ///
46     /// **Known problems:** Only checks for lints associated using the
47     /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
48     ///
49     /// **Example:**
50     /// ```rust,ignore
51     /// declare_lint! { pub LINT_1, ... }
52     /// declare_lint! { pub LINT_2, ... }
53     /// declare_lint! { pub FORGOTTEN_LINT, ... }
54     /// // ...
55     /// declare_lint_pass!(Pass => [LINT_1, LINT_2]);
56     /// // missing FORGOTTEN_LINT
57     /// ```
58     pub LINT_WITHOUT_LINT_PASS,
59     internal,
60     "declaring a lint without associating it in a LintPass"
61 }
62
63 declare_clippy_lint! {
64     /// **What it does:** Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
65     /// variant of the function.
66     ///
67     /// **Why is this bad?** The `utils::*` variants also add a link to the Clippy documentation to the
68     /// warning/error messages.
69     ///
70     /// **Known problems:** None.
71     ///
72     /// **Example:**
73     /// Bad:
74     /// ```rust,ignore
75     /// cx.span_lint(LINT_NAME, "message");
76     /// ```
77     ///
78     /// Good:
79     /// ```rust,ignore
80     /// utils::span_lint(cx, LINT_NAME, "message");
81     /// ```
82     pub COMPILER_LINT_FUNCTIONS,
83     internal,
84     "usage of the lint functions of the compiler instead of the utils::* variant"
85 }
86
87 declare_clippy_lint! {
88     /// **What it does:** Checks for calls to `cx.outer().expn_data()` and suggests to use
89     /// the `cx.outer_expn_data()`
90     ///
91     /// **Why is this bad?** `cx.outer_expn_data()` is faster and more concise.
92     ///
93     /// **Known problems:** None.
94     ///
95     /// **Example:**
96     /// Bad:
97     /// ```rust,ignore
98     /// expr.span.ctxt().outer().expn_data()
99     /// ```
100     ///
101     /// Good:
102     /// ```rust,ignore
103     /// expr.span.ctxt().outer_expn_data()
104     /// ```
105     pub OUTER_EXPN_EXPN_DATA,
106     internal,
107     "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
108 }
109
110 declare_clippy_lint! {
111     /// **What it does:** Not an actual lint. This lint is only meant for testing our customized internal compiler
112     /// error message by calling `panic`.
113     ///
114     /// **Why is this bad?** ICE in large quantities can damage your teeth
115     ///
116     /// **Known problems:** None
117     ///
118     /// **Example:**
119     /// Bad:
120     /// ```rust,ignore
121     /// 🍦🍦🍦🍦🍦
122     /// ```
123     pub PRODUCE_ICE,
124     internal,
125     "this message should not appear anywhere as we ICE before and don't emit the lint"
126 }
127
128 declare_clippy_lint! {
129     /// **What it does:** Checks for cases of an auto-generated lint without an updated description,
130     /// i.e. `default lint description`.
131     ///
132     /// **Why is this bad?** Indicates that the lint is not finished.
133     ///
134     /// **Known problems:** None
135     ///
136     /// **Example:**
137     /// Bad:
138     /// ```rust,ignore
139     /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
140     /// ```
141     ///
142     /// Good:
143     /// ```rust,ignore
144     /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
145     /// ```
146     pub DEFAULT_LINT,
147     internal,
148     "found 'default lint description' in a lint declaration"
149 }
150
151 declare_clippy_lint! {
152     /// **What it does:** Lints `span_lint_and_then` function calls, where the
153     /// closure argument has only one statement and that statement is a method
154     /// call to `span_suggestion`, `span_help`, `span_note` (using the same
155     /// span), `help` or `note`.
156     ///
157     /// These usages of `span_lint_and_then` should be replaced with one of the
158     /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
159     /// `span_lint_and_note`.
160     ///
161     /// **Why is this bad?** Using the wrapper `span_lint_and_*` functions, is more
162     /// convenient, readable and less error prone.
163     ///
164     /// **Known problems:** None
165     ///
166     /// *Example:**
167     /// Bad:
168     /// ```rust,ignore
169     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
170     ///     diag.span_suggestion(
171     ///         expr.span,
172     ///         help_msg,
173     ///         sugg.to_string(),
174     ///         Applicability::MachineApplicable,
175     ///     );
176     /// });
177     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
178     ///     diag.span_help(expr.span, help_msg);
179     /// });
180     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
181     ///     diag.help(help_msg);
182     /// });
183     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
184     ///     diag.span_note(expr.span, note_msg);
185     /// });
186     /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
187     ///     diag.note(note_msg);
188     /// });
189     /// ```
190     ///
191     /// Good:
192     /// ```rust,ignore
193     /// span_lint_and_sugg(
194     ///     cx,
195     ///     TEST_LINT,
196     ///     expr.span,
197     ///     lint_msg,
198     ///     help_msg,
199     ///     sugg.to_string(),
200     ///     Applicability::MachineApplicable,
201     /// );
202     /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
203     /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
204     /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
205     /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
206     /// ```
207     pub COLLAPSIBLE_SPAN_LINT_CALLS,
208     internal,
209     "found collapsible `span_lint_and_then` calls"
210 }
211
212 declare_clippy_lint! {
213     /// **What it does:** Checks for calls to `utils::match_type()` on a type diagnostic item
214     /// and suggests to use `utils::is_type_diagnostic_item()` instead.
215     ///
216     /// **Why is this bad?** `utils::is_type_diagnostic_item()` does not require hardcoded paths.
217     ///
218     /// **Known problems:** None.
219     ///
220     /// **Example:**
221     /// Bad:
222     /// ```rust,ignore
223     /// utils::match_type(cx, ty, &paths::VEC)
224     /// ```
225     ///
226     /// Good:
227     /// ```rust,ignore
228     /// utils::is_type_diagnostic_item(cx, ty, sym::vec_type)
229     /// ```
230     pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
231     internal,
232     "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
233 }
234
235 declare_clippy_lint! {
236     /// **What it does:**
237     /// Checks the paths module for invalid paths.
238     ///
239     /// **Why is this bad?**
240     /// It indicates a bug in the code.
241     ///
242     /// **Known problems:** None.
243     ///
244     /// **Example:** None.
245     pub INVALID_PATHS,
246     internal,
247     "invalid path"
248 }
249
250 declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
251
252 impl EarlyLintPass for ClippyLintsInternal {
253     fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) {
254         if let Some(utils) = krate
255             .module
256             .items
257             .iter()
258             .find(|item| item.ident.name.as_str() == "utils")
259         {
260             if let ItemKind::Mod(ref utils_mod) = utils.kind {
261                 if let Some(paths) = utils_mod.items.iter().find(|item| item.ident.name.as_str() == "paths") {
262                     if let ItemKind::Mod(ref paths_mod) = paths.kind {
263                         let mut last_name: Option<SymbolStr> = None;
264                         for item in &*paths_mod.items {
265                             let name = item.ident.as_str();
266                             if let Some(ref last_name) = last_name {
267                                 if **last_name > *name {
268                                     span_lint(
269                                         cx,
270                                         CLIPPY_LINTS_INTERNAL,
271                                         item.span,
272                                         "this constant should be before the previous constant due to lexical \
273                                          ordering",
274                                     );
275                                 }
276                             }
277                             last_name = Some(name);
278                         }
279                     }
280                 }
281             }
282         }
283     }
284 }
285
286 #[derive(Clone, Debug, Default)]
287 pub struct LintWithoutLintPass {
288     declared_lints: FxHashMap<Symbol, Span>,
289     registered_lints: FxHashSet<Symbol>,
290 }
291
292 impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]);
293
294 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
295     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
296         if !run_lints(cx, &[DEFAULT_LINT], item.hir_id) {
297             return;
298         }
299
300         if let hir::ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind {
301             if is_lint_ref_type(cx, ty) {
302                 let expr = &cx.tcx.hir().body(body_id).value;
303                 if_chain! {
304                     if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind;
305                     if let ExprKind::Struct(_, ref fields, _) = inner_exp.kind;
306                     let field = fields
307                         .iter()
308                         .find(|f| f.ident.as_str() == "desc")
309                         .expect("lints must have a description field");
310                     if let ExprKind::Lit(Spanned {
311                         node: LitKind::Str(ref sym, _),
312                         ..
313                     }) = field.expr.kind;
314                     if sym.as_str() == "default lint description";
315
316                     then {
317                         span_lint(
318                             cx,
319                             DEFAULT_LINT,
320                             item.span,
321                             &format!("the lint `{}` has the default lint description", item.ident.name),
322                         );
323                     }
324                 }
325                 self.declared_lints.insert(item.ident.name, item.span);
326             }
327         } else if is_expn_of(item.span, "impl_lint_pass").is_some()
328             || is_expn_of(item.span, "declare_lint_pass").is_some()
329         {
330             if let hir::ItemKind::Impl {
331                 of_trait: None,
332                 items: ref impl_item_refs,
333                 ..
334             } = item.kind
335             {
336                 let mut collector = LintCollector {
337                     output: &mut self.registered_lints,
338                     cx,
339                 };
340                 let body_id = cx.tcx.hir().body_owned_by(
341                     impl_item_refs
342                         .iter()
343                         .find(|iiref| iiref.ident.as_str() == "get_lints")
344                         .expect("LintPass needs to implement get_lints")
345                         .id
346                         .hir_id,
347                 );
348                 collector.visit_expr(&cx.tcx.hir().body(body_id).value);
349             }
350         }
351     }
352
353     fn check_crate_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Crate<'_>) {
354         if !run_lints(cx, &[LINT_WITHOUT_LINT_PASS], CRATE_HIR_ID) {
355             return;
356         }
357
358         for (lint_name, &lint_span) in &self.declared_lints {
359             // When using the `declare_tool_lint!` macro, the original `lint_span`'s
360             // file points to "<rustc macros>".
361             // `compiletest-rs` thinks that's an error in a different file and
362             // just ignores it. This causes the test in compile-fail/lint_pass
363             // not able to capture the error.
364             // Therefore, we need to climb the macro expansion tree and find the
365             // actual span that invoked `declare_tool_lint!`:
366             let lint_span = lint_span.ctxt().outer_expn_data().call_site;
367
368             if !self.registered_lints.contains(lint_name) {
369                 span_lint(
370                     cx,
371                     LINT_WITHOUT_LINT_PASS,
372                     lint_span,
373                     &format!("the lint `{}` is not added to any `LintPass`", lint_name),
374                 );
375             }
376         }
377     }
378 }
379
380 fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool {
381     if let TyKind::Rptr(
382         _,
383         MutTy {
384             ty: ref inner,
385             mutbl: Mutability::Not,
386         },
387     ) = ty.kind
388     {
389         if let TyKind::Path(ref path) = inner.kind {
390             if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
391                 return match_def_path(cx, def_id, &paths::LINT);
392             }
393         }
394     }
395
396     false
397 }
398
399 struct LintCollector<'a, 'tcx> {
400     output: &'a mut FxHashSet<Symbol>,
401     cx: &'a LateContext<'tcx>,
402 }
403
404 impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
405     type Map = Map<'tcx>;
406
407     fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
408         if path.segments.len() == 1 {
409             self.output.insert(path.segments[0].ident.name);
410         }
411     }
412
413     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
414         NestedVisitorMap::All(self.cx.tcx.hir())
415     }
416 }
417
418 #[derive(Clone, Default)]
419 pub struct CompilerLintFunctions {
420     map: FxHashMap<&'static str, &'static str>,
421 }
422
423 impl CompilerLintFunctions {
424     #[must_use]
425     pub fn new() -> Self {
426         let mut map = FxHashMap::default();
427         map.insert("span_lint", "utils::span_lint");
428         map.insert("struct_span_lint", "utils::span_lint");
429         map.insert("lint", "utils::span_lint");
430         map.insert("span_lint_note", "utils::span_lint_and_note");
431         map.insert("span_lint_help", "utils::span_lint_and_help");
432         Self { map }
433     }
434 }
435
436 impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
437
438 impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
439     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
440         if !run_lints(cx, &[COMPILER_LINT_FUNCTIONS], expr.hir_id) {
441             return;
442         }
443
444         if_chain! {
445             if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind;
446             let fn_name = path.ident;
447             if let Some(sugg) = self.map.get(&*fn_name.as_str());
448             let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
449             if match_type(cx, ty, &paths::EARLY_CONTEXT)
450                 || match_type(cx, ty, &paths::LATE_CONTEXT);
451             then {
452                 span_lint_and_help(
453                     cx,
454                     COMPILER_LINT_FUNCTIONS,
455                     path.ident.span,
456                     "usage of a compiler lint function",
457                     None,
458                     &format!("please use the Clippy variant of this function: `{}`", sugg),
459                 );
460             }
461         }
462     }
463 }
464
465 declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
466
467 impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
468     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
469         if !run_lints(cx, &[OUTER_EXPN_EXPN_DATA], expr.hir_id) {
470             return;
471         }
472
473         let (method_names, arg_lists, spans) = method_calls(expr, 2);
474         let method_names: Vec<SymbolStr> = method_names.iter().map(|s| s.as_str()).collect();
475         let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect();
476         if_chain! {
477             if let ["expn_data", "outer_expn"] = method_names.as_slice();
478             let args = arg_lists[1];
479             if args.len() == 1;
480             let self_arg = &args[0];
481             let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
482             if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
483             then {
484                 span_lint_and_sugg(
485                     cx,
486                     OUTER_EXPN_EXPN_DATA,
487                     spans[1].with_hi(expr.span.hi()),
488                     "usage of `outer_expn().expn_data()`",
489                     "try",
490                     "outer_expn_data()".to_string(),
491                     Applicability::MachineApplicable,
492                 );
493             }
494         }
495     }
496 }
497
498 declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
499
500 impl EarlyLintPass for ProduceIce {
501     fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
502         if is_trigger_fn(fn_kind) {
503             panic!("Would you like some help with that?");
504         }
505     }
506 }
507
508 fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
509     match fn_kind {
510         FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
511         FnKind::Closure(..) => false,
512     }
513 }
514
515 declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
516
517 impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
518     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
519         if !run_lints(cx, &[COLLAPSIBLE_SPAN_LINT_CALLS], expr.hir_id) {
520             return;
521         }
522
523         if_chain! {
524             if let ExprKind::Call(ref func, ref and_then_args) = expr.kind;
525             if let ExprKind::Path(ref path) = func.kind;
526             if match_qpath(path, &["span_lint_and_then"]);
527             if and_then_args.len() == 5;
528             if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
529             let body = cx.tcx.hir().body(*body_id);
530             if let ExprKind::Block(block, _) = &body.value.kind;
531             let stmts = &block.stmts;
532             if stmts.len() == 1 && block.expr.is_none();
533             if let StmtKind::Semi(only_expr) = &stmts[0].kind;
534             if let ExprKind::MethodCall(ref ps, _, ref span_call_args, _) = &only_expr.kind;
535             let and_then_snippets = get_and_then_snippets(cx, and_then_args);
536             let mut sle = SpanlessEq::new(cx).deny_side_effects();
537             then {
538                 match &*ps.ident.as_str() {
539                     "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
540                         suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
541                     },
542                     "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
543                         let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
544                         suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
545                     },
546                     "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
547                         let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
548                         suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
549                     },
550                     "help" => {
551                         let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
552                         suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
553                     }
554                     "note" => {
555                         let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
556                         suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
557                     }
558                     _  => (),
559                 }
560             }
561         }
562     }
563 }
564
565 struct AndThenSnippets<'a> {
566     cx: Cow<'a, str>,
567     lint: Cow<'a, str>,
568     span: Cow<'a, str>,
569     msg: Cow<'a, str>,
570 }
571
572 fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> {
573     let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx");
574     let lint_snippet = snippet(cx, and_then_snippets[1].span, "..");
575     let span_snippet = snippet(cx, and_then_snippets[2].span, "span");
576     let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#);
577
578     AndThenSnippets {
579         cx: cx_snippet,
580         lint: lint_snippet,
581         span: span_snippet,
582         msg: msg_snippet,
583     }
584 }
585
586 struct SpanSuggestionSnippets<'a> {
587     help: Cow<'a, str>,
588     sugg: Cow<'a, str>,
589     applicability: Cow<'a, str>,
590 }
591
592 fn span_suggestion_snippets<'a, 'hir>(
593     cx: &LateContext<'_>,
594     span_call_args: &'hir [Expr<'hir>],
595 ) -> SpanSuggestionSnippets<'a> {
596     let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
597     let sugg_snippet = snippet(cx, span_call_args[3].span, "..");
598     let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable");
599
600     SpanSuggestionSnippets {
601         help: help_snippet,
602         sugg: sugg_snippet,
603         applicability: applicability_snippet,
604     }
605 }
606
607 fn suggest_suggestion(
608     cx: &LateContext<'_>,
609     expr: &Expr<'_>,
610     and_then_snippets: &AndThenSnippets<'_>,
611     span_suggestion_snippets: &SpanSuggestionSnippets<'_>,
612 ) {
613     span_lint_and_sugg(
614         cx,
615         COLLAPSIBLE_SPAN_LINT_CALLS,
616         expr.span,
617         "this call is collapsible",
618         "collapse into",
619         format!(
620             "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})",
621             and_then_snippets.cx,
622             and_then_snippets.lint,
623             and_then_snippets.span,
624             and_then_snippets.msg,
625             span_suggestion_snippets.help,
626             span_suggestion_snippets.sugg,
627             span_suggestion_snippets.applicability
628         ),
629         Applicability::MachineApplicable,
630     );
631 }
632
633 fn suggest_help(
634     cx: &LateContext<'_>,
635     expr: &Expr<'_>,
636     and_then_snippets: &AndThenSnippets<'_>,
637     help: &str,
638     with_span: bool,
639 ) {
640     let option_span = if with_span {
641         format!("Some({})", and_then_snippets.span)
642     } else {
643         "None".to_string()
644     };
645
646     span_lint_and_sugg(
647         cx,
648         COLLAPSIBLE_SPAN_LINT_CALLS,
649         expr.span,
650         "this call is collapsible",
651         "collapse into",
652         format!(
653             "span_lint_and_help({}, {}, {}, {}, {}, {})",
654             and_then_snippets.cx,
655             and_then_snippets.lint,
656             and_then_snippets.span,
657             and_then_snippets.msg,
658             &option_span,
659             help
660         ),
661         Applicability::MachineApplicable,
662     );
663 }
664
665 fn suggest_note(
666     cx: &LateContext<'_>,
667     expr: &Expr<'_>,
668     and_then_snippets: &AndThenSnippets<'_>,
669     note: &str,
670     with_span: bool,
671 ) {
672     let note_span = if with_span {
673         format!("Some({})", and_then_snippets.span)
674     } else {
675         "None".to_string()
676     };
677
678     span_lint_and_sugg(
679         cx,
680         COLLAPSIBLE_SPAN_LINT_CALLS,
681         expr.span,
682         "this call is collspible",
683         "collapse into",
684         format!(
685             "span_lint_and_note({}, {}, {}, {}, {}, {})",
686             and_then_snippets.cx,
687             and_then_snippets.lint,
688             and_then_snippets.span,
689             and_then_snippets.msg,
690             note_span,
691             note
692         ),
693         Applicability::MachineApplicable,
694     );
695 }
696
697 declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]);
698
699 impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
700     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
701         if !run_lints(cx, &[MATCH_TYPE_ON_DIAGNOSTIC_ITEM], expr.hir_id) {
702             return;
703         }
704
705         if_chain! {
706             // Check if this is a call to utils::match_type()
707             if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
708             if let ExprKind::Path(fn_qpath) = &fn_path.kind;
709             if match_qpath(&fn_qpath, &["utils", "match_type"]);
710             // Extract the path to the matched type
711             if let Some(segments) = path_to_matched_type(cx, ty_path);
712             let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
713             if let Some(ty_did) = path_to_res(cx, &segments[..]).and_then(|res| res.opt_def_id());
714             // Check if the matched type is a diagnostic item
715             let diag_items = cx.tcx.diagnostic_items(ty_did.krate);
716             if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None });
717             then {
718                 let cx_snippet = snippet(cx, context.span, "_");
719                 let ty_snippet = snippet(cx, ty.span, "_");
720
721                 span_lint_and_sugg(
722                     cx,
723                     MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
724                     expr.span,
725                     "usage of `utils::match_type()` on a type diagnostic item",
726                     "try",
727                     format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
728                     Applicability::MaybeIncorrect,
729                 );
730             }
731         }
732     }
733 }
734
735 fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<SymbolStr>> {
736     use rustc_hir::ItemKind;
737
738     match &expr.kind {
739         ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
740         ExprKind::Path(qpath) => match qpath_res(cx, qpath, expr.hir_id) {
741             Res::Local(hir_id) => {
742                 let parent_id = cx.tcx.hir().get_parent_node(hir_id);
743                 if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) {
744                     if let Some(init) = local.init {
745                         return path_to_matched_type(cx, init);
746                     }
747                 }
748             },
749             Res::Def(DefKind::Const | DefKind::Static, def_id) => {
750                 if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
751                     if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
752                         let body = cx.tcx.hir().body(body_id);
753                         return path_to_matched_type(cx, &body.value);
754                     }
755                 }
756             },
757             _ => {},
758         },
759         ExprKind::Array(exprs) => {
760             let segments: Vec<SymbolStr> = exprs
761                 .iter()
762                 .filter_map(|expr| {
763                     if let ExprKind::Lit(lit) = &expr.kind {
764                         if let LitKind::Str(sym, _) = lit.node {
765                             return Some(sym.as_str());
766                         }
767                     }
768
769                     None
770                 })
771                 .collect();
772
773             if segments.len() == exprs.len() {
774                 return Some(segments);
775             }
776         },
777         _ => {},
778     }
779
780     None
781 }
782
783 // This is not a complete resolver for paths. It works on all the paths currently used in the paths
784 // module.  That's all it does and all it needs to do.
785 pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
786     if path_to_res(cx, path).is_some() {
787         return true;
788     }
789
790     // Some implementations can't be found by `path_to_res`, particularly inherent
791     // implementations of native types. Check lang items.
792     let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
793     let lang_items = cx.tcx.lang_items();
794     for lang_item in lang_items.items() {
795         if let Some(def_id) = lang_item {
796             let lang_item_path = cx.get_def_path(*def_id);
797             if path_syms.starts_with(&lang_item_path) {
798                 if let [item] = &path_syms[lang_item_path.len()..] {
799                     for child in cx.tcx.item_children(*def_id) {
800                         if child.ident.name == *item {
801                             return true;
802                         }
803                     }
804                 }
805             }
806         }
807     }
808
809     false
810 }
811
812 declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
813
814 impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
815     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
816         let local_def_id = &cx.tcx.parent_module(item.hir_id);
817         let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
818         if_chain! {
819             if mod_name.as_str() == "paths";
820             if let hir::ItemKind::Const(ty, body_id) = item.kind;
821             let ty = hir_ty_to_ty(cx.tcx, ty);
822             if let ty::Array(el_ty, _) = &ty.kind();
823             if let ty::Ref(_, el_ty, _) = &el_ty.kind();
824             if el_ty.is_str();
825             let body = cx.tcx.hir().body(body_id);
826             let typeck_results = cx.tcx.typeck_body(body_id);
827             if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value);
828             let path: Vec<&str> = path.iter().map(|x| {
829                     if let Constant::Str(s) = x {
830                         s.as_str()
831                     } else {
832                         // We checked the type of the constant above
833                         unreachable!()
834                     }
835                 }).collect();
836             if !check_path(cx, &path[..]);
837             then {
838                 span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path");
839             }
840         }
841     }
842 }