2 is_expn_of, match_def_path, match_type, method_calls, paths, span_help_and_lint, span_lint, span_lint_and_sugg,
5 use if_chain::if_chain;
7 use rustc::hir::def::{DefKind, Res};
8 use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
10 use rustc::lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintArray, LintPass};
11 use rustc::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
12 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
13 use rustc_errors::Applicability;
14 use syntax::ast::{Crate as AstCrate, ItemKind, Name};
15 use syntax::source_map::Span;
16 use syntax_pos::symbol::SymbolStr;
18 declare_clippy_lint! {
19 /// **What it does:** Checks for various things we like to keep tidy in clippy.
21 /// **Why is this bad?** We like to pretend we're an example of tidy code.
23 /// **Known problems:** None.
25 /// **Example:** Wrong ordering of the util::paths constants.
26 pub CLIPPY_LINTS_INTERNAL,
28 "various things that will negatively affect your clippy experience"
31 declare_clippy_lint! {
32 /// **What it does:** Ensures every lint is associated to a `LintPass`.
34 /// **Why is this bad?** The compiler only knows lints via a `LintPass`. Without
35 /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
36 /// know the name of the lint.
38 /// **Known problems:** Only checks for lints associated using the
39 /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
43 /// declare_lint! { pub LINT_1, ... }
44 /// declare_lint! { pub LINT_2, ... }
45 /// declare_lint! { pub FORGOTTEN_LINT, ... }
47 /// declare_lint_pass!(Pass => [LINT_1, LINT_2]);
48 /// // missing FORGOTTEN_LINT
50 pub LINT_WITHOUT_LINT_PASS,
52 "declaring a lint without associating it in a LintPass"
55 declare_clippy_lint! {
56 /// **What it does:** Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
57 /// variant of the function.
59 /// **Why is this bad?** The `utils::*` variants also add a link to the Clippy documentation to the
60 /// warning/error messages.
62 /// **Known problems:** None.
67 /// cx.span_lint(LINT_NAME, "message");
72 /// utils::span_lint(cx, LINT_NAME, "message");
74 pub COMPILER_LINT_FUNCTIONS,
76 "usage of the lint functions of the compiler instead of the utils::* variant"
79 declare_clippy_lint! {
80 /// **What it does:** Checks for calls to `cx.outer().expn_data()` and suggests to use
81 /// the `cx.outer_expn_data()`
83 /// **Why is this bad?** `cx.outer_expn_data()` is faster and more concise.
85 /// **Known problems:** None.
90 /// expr.span.ctxt().outer().expn_data()
95 /// expr.span.ctxt().outer_expn_data()
97 pub OUTER_EXPN_EXPN_DATA,
99 "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
102 declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
104 impl EarlyLintPass for ClippyLintsInternal {
105 fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) {
106 if let Some(utils) = krate
110 .find(|item| item.ident.name.as_str() == "utils")
112 if let ItemKind::Mod(ref utils_mod) = utils.kind {
113 if let Some(paths) = utils_mod.items.iter().find(|item| item.ident.name.as_str() == "paths") {
114 if let ItemKind::Mod(ref paths_mod) = paths.kind {
115 let mut last_name: Option<SymbolStr> = None;
116 for item in &*paths_mod.items {
117 let name = item.ident.as_str();
118 if let Some(ref last_name) = last_name {
119 if **last_name > *name {
122 CLIPPY_LINTS_INTERNAL,
124 "this constant should be before the previous constant due to lexical \
129 last_name = Some(name);
138 #[derive(Clone, Debug, Default)]
139 pub struct LintWithoutLintPass {
140 declared_lints: FxHashMap<Name, Span>,
141 registered_lints: FxHashSet<Name>,
144 impl_lint_pass!(LintWithoutLintPass => [LINT_WITHOUT_LINT_PASS]);
146 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LintWithoutLintPass {
147 fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
148 if let hir::ItemKind::Static(ref ty, Mutability::Immutable, _) = item.kind {
149 if is_lint_ref_type(cx, ty) {
150 self.declared_lints.insert(item.ident.name, item.span);
152 } else if is_expn_of(item.span, "impl_lint_pass").is_some()
153 || is_expn_of(item.span, "declare_lint_pass").is_some()
155 if let hir::ItemKind::Impl(.., None, _, ref impl_item_refs) = item.kind {
156 let mut collector = LintCollector {
157 output: &mut self.registered_lints,
160 let body_id = cx.tcx.hir().body_owned_by(
163 .find(|iiref| iiref.ident.as_str() == "get_lints")
164 .expect("LintPass needs to implement get_lints")
168 collector.visit_expr(&cx.tcx.hir().body(body_id).value);
173 fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, _: &'tcx Crate) {
174 for (lint_name, &lint_span) in &self.declared_lints {
175 // When using the `declare_tool_lint!` macro, the original `lint_span`'s
176 // file points to "<rustc macros>".
177 // `compiletest-rs` thinks that's an error in a different file and
178 // just ignores it. This causes the test in compile-fail/lint_pass
179 // not able to capture the error.
180 // Therefore, we need to climb the macro expansion tree and find the
181 // actual span that invoked `declare_tool_lint!`:
182 let lint_span = lint_span.ctxt().outer_expn_data().call_site;
184 if !self.registered_lints.contains(lint_name) {
187 LINT_WITHOUT_LINT_PASS,
189 &format!("the lint `{}` is not added to any `LintPass`", lint_name),
196 fn is_lint_ref_type<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &Ty) -> bool {
201 mutbl: Mutability::Immutable,
205 if let TyKind::Path(ref path) = inner.kind {
206 if let Res::Def(DefKind::Struct, def_id) = cx.tables.qpath_res(path, inner.hir_id) {
207 return match_def_path(cx, def_id, &paths::LINT);
215 struct LintCollector<'a, 'tcx> {
216 output: &'a mut FxHashSet<Name>,
217 cx: &'a LateContext<'a, 'tcx>,
220 impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
221 fn visit_expr(&mut self, expr: &'tcx Expr) {
222 walk_expr(self, expr);
225 fn visit_path(&mut self, path: &'tcx Path, _: HirId) {
226 if path.segments.len() == 1 {
227 self.output.insert(path.segments[0].ident.name);
230 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
231 NestedVisitorMap::All(&self.cx.tcx.hir())
235 #[derive(Clone, Default)]
236 pub struct CompilerLintFunctions {
237 map: FxHashMap<&'static str, &'static str>,
240 impl CompilerLintFunctions {
242 pub fn new() -> Self {
243 let mut map = FxHashMap::default();
244 map.insert("span_lint", "utils::span_lint");
245 map.insert("struct_span_lint", "utils::span_lint");
246 map.insert("lint", "utils::span_lint");
247 map.insert("span_lint_note", "utils::span_note_and_lint");
248 map.insert("span_lint_help", "utils::span_help_and_lint");
253 impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
255 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CompilerLintFunctions {
256 fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
258 if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind;
259 let fn_name = path.ident;
260 if let Some(sugg) = self.map.get(&*fn_name.as_str());
261 let ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0]));
262 if match_type(cx, ty, &paths::EARLY_CONTEXT)
263 || match_type(cx, ty, &paths::LATE_CONTEXT);
267 COMPILER_LINT_FUNCTIONS,
269 "usage of a compiler lint function",
270 &format!("please use the Clippy variant of this function: `{}`", sugg),
277 declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
279 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OuterExpnDataPass {
280 fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
281 let (method_names, arg_lists, spans) = method_calls(expr, 2);
282 let method_names: Vec<SymbolStr> = method_names.iter().map(|s| s.as_str()).collect();
283 let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect();
285 if let ["expn_data", "outer_expn"] = method_names.as_slice();
286 let args = arg_lists[1];
288 let self_arg = &args[0];
289 let self_ty = walk_ptrs_ty(cx.tables.expr_ty(self_arg));
290 if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
294 OUTER_EXPN_EXPN_DATA,
295 spans[1].with_hi(expr.span.hi()),
296 "usage of `outer_expn().expn_data()`",
298 "outer_expn_data()".to_string(),
299 Applicability::MachineApplicable,