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