3 use rustc
::hir
::intravisit
::{walk_expr, NestedVisitorMap, Visitor}
;
4 use utils
::{match_qpath, paths, span_lint}
;
5 use syntax
::symbol
::InternedString
;
6 use syntax
::ast
::{Crate as AstCrate, ItemKind, Name, NodeId}
;
7 use syntax
::codemap
::Span
;
8 use std
::collections
::{HashMap, HashSet}
;
11 /// **What it does:** Checks for various things we like to keep tidy in clippy.
13 /// **Why is this bad?** We like to pretend we're an example of tidy code.
15 /// **Known problems:** None.
17 /// **Example:** Wrong ordering of the util::paths constants.
19 pub CLIPPY_LINTS_INTERNAL
,
21 "various things that will negatively affect your clippy experience"
25 /// **What it does:** Ensures every lint is associated to a `LintPass`.
27 /// **Why is this bad?** The compiler only knows lints via a `LintPass`. Without
28 /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
29 /// know the name of the lint.
31 /// **Known problems:** Only checks for lints associated using the `lint_array!`
36 /// declare_lint! { pub LINT_1, ... }
37 /// declare_lint! { pub LINT_2, ... }
38 /// declare_lint! { pub FORGOTTEN_LINT, ... }
41 /// impl LintPass for Pass {
42 /// fn get_lints(&self) -> LintArray {
43 /// lint_array![LINT_1, LINT_2]
44 /// // missing FORGOTTEN_LINT
49 pub LINT_WITHOUT_LINT_PASS
,
51 "declaring a lint without associating it in a LintPass"
55 #[derive(Copy, Clone)]
58 impl LintPass
for Clippy
{
59 fn get_lints(&self) -> LintArray
{
60 lint_array
!(CLIPPY_LINTS_INTERNAL
)
64 impl EarlyLintPass
for Clippy
{
65 fn check_crate(&mut self, cx
: &EarlyContext
, krate
: &AstCrate
) {
66 if let Some(utils
) = krate
70 .find(|item
| item
.ident
.name
== "utils")
72 if let ItemKind
::Mod(ref utils_mod
) = utils
.node
{
73 if let Some(paths
) = utils_mod
76 .find(|item
| item
.ident
.name
== "paths")
78 if let ItemKind
::Mod(ref paths_mod
) = paths
.node
{
79 let mut last_name
: Option
<InternedString
> = None
;
80 for item
in &paths_mod
.items
{
81 let name
= item
.ident
.name
.as_str();
82 if let Some(ref last_name
) = last_name
{
83 if **last_name
> *name
{
86 CLIPPY_LINTS_INTERNAL
,
88 "this constant should be before the previous constant due to lexical \
93 last_name
= Some(name
);
104 #[derive(Clone, Debug, Default)]
105 pub struct LintWithoutLintPass
{
106 declared_lints
: HashMap
<Name
, Span
>,
107 registered_lints
: HashSet
<Name
>,
111 impl LintPass
for LintWithoutLintPass
{
112 fn get_lints(&self) -> LintArray
{
113 lint_array
!(LINT_WITHOUT_LINT_PASS
)
118 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for LintWithoutLintPass
{
119 fn check_item(&mut self, cx
: &LateContext
<'a
, 'tcx
>, item
: &'tcx Item
) {
120 if let ItemStatic(ref ty
, MutImmutable
, body_id
) = item
.node
{
121 if is_lint_ref_type(ty
) {
122 self.declared_lints
.insert(item
.name
, item
.span
);
123 } else if is_lint_array_type(ty
) && item
.vis
== Visibility
::Inherited
&& item
.name
== "ARRAY" {
124 let mut collector
= LintCollector
{
125 output
: &mut self.registered_lints
,
128 collector
.visit_expr(&cx
.tcx
.hir
.body(body_id
).value
);
133 fn check_crate_post(&mut self, cx
: &LateContext
<'a
, 'tcx
>, _
: &'tcx Crate
) {
134 for (lint_name
, &lint_span
) in &self.declared_lints
{
135 // When using the `declare_lint!` macro, the original `lint_span`'s
136 // file points to "<rustc macros>".
137 // `compiletest-rs` thinks that's an error in a different file and
138 // just ignores it. This causes the test in compile-fail/lint_pass
139 // not able to capture the error.
140 // Therefore, we need to climb the macro expansion tree and find the
141 // actual span that invoked `declare_lint!`:
142 let lint_span
= lint_span
146 .map(|ei
| ei
.call_site
)
147 .expect("unable to get call_site");
149 if !self.registered_lints
.contains(lint_name
) {
152 LINT_WITHOUT_LINT_PASS
,
154 &format
!("the lint `{}` is not added to any `LintPass`", lint_name
),
162 fn is_lint_ref_type(ty
: &Ty
) -> bool
{
171 if let TyPath(ref path
) = inner
.node
{
172 return match_qpath(path
, &paths
::LINT
);
179 fn is_lint_array_type(ty
: &Ty
) -> bool
{
180 if let TyPath(ref path
) = ty
.node
{
181 match_qpath(path
, &paths
::LINT_ARRAY
)
187 struct LintCollector
<'a
, 'tcx
: 'a
> {
188 output
: &'a
mut HashSet
<Name
>,
189 cx
: &'a LateContext
<'a
, 'tcx
>,
192 impl<'a
, 'tcx
: 'a
> Visitor
<'tcx
> for LintCollector
<'a
, 'tcx
> {
193 fn visit_expr(&mut self, expr
: &'tcx Expr
) {
194 walk_expr(self, expr
);
197 fn visit_path(&mut self, path
: &'tcx Path
, _
: NodeId
) {
198 if path
.segments
.len() == 1 {
199 self.output
.insert(path
.segments
[0].name
);
202 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
203 NestedVisitorMap
::All(&self.cx
.tcx
.hir
)