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