]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/missing_doc.rs
Missing docs: don't require documenting Global Asm items.
[rust.git] / clippy_lints / src / missing_doc.rs
1 // Note: More specifically this lint is largely inspired (aka copied) from
2 // *rustc*'s
3 // [`missing_doc`].
4 //
5 // [`missing_doc`]: https://github.com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.rs#L246
6 //
7
8 use crate::utils::{in_macro, span_lint};
9 use rustc::hir;
10 use rustc::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
11 use rustc::ty;
12 use rustc::{declare_tool_lint, lint_array};
13 use syntax::ast;
14 use syntax::attr;
15 use syntax::source_map::Span;
16
17 /// **What it does:** Warns if there is missing doc for any documentable item
18 /// (public or private).
19 ///
20 /// **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS`
21 /// allowed-by-default lint for
22 /// public members, but has no way to enforce documentation of private items.
23 /// This lint fixes that.
24 ///
25 /// **Known problems:** None.
26 declare_clippy_lint! {
27     pub MISSING_DOCS_IN_PRIVATE_ITEMS,
28     restriction,
29     "detects missing documentation for public and private members"
30 }
31
32 pub struct MissingDoc {
33     /// Stack of whether #[doc(hidden)] is set
34     /// at each level which has lint attributes.
35     doc_hidden_stack: Vec<bool>,
36 }
37
38 impl ::std::default::Default for MissingDoc {
39     fn default() -> Self {
40         Self::new()
41     }
42 }
43
44 impl MissingDoc {
45     pub fn new() -> Self {
46         Self {
47             doc_hidden_stack: vec![false],
48         }
49     }
50
51     fn doc_hidden(&self) -> bool {
52         *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
53     }
54
55     fn check_missing_docs_attrs(
56         &self,
57         cx: &LateContext<'_, '_>,
58         attrs: &[ast::Attribute],
59         sp: Span,
60         desc: &'static str,
61     ) {
62         // If we're building a test harness, then warning about
63         // documentation is probably not really relevant right now.
64         if cx.sess().opts.test {
65             return;
66         }
67
68         // `#[doc(hidden)]` disables missing_docs check.
69         if self.doc_hidden() {
70             return;
71         }
72
73         if in_macro(sp) {
74             return;
75         }
76
77         let has_doc = attrs.iter().any(|a| a.is_value_str() && a.name() == "doc");
78         if !has_doc {
79             span_lint(
80                 cx,
81                 MISSING_DOCS_IN_PRIVATE_ITEMS,
82                 sp,
83                 &format!("missing documentation for {}", desc),
84             );
85         }
86     }
87 }
88
89 impl LintPass for MissingDoc {
90     fn get_lints(&self) -> LintArray {
91         lint_array![MISSING_DOCS_IN_PRIVATE_ITEMS]
92     }
93 }
94
95 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
96     fn enter_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, attrs: &'tcx [ast::Attribute]) {
97         let doc_hidden = self.doc_hidden()
98             || attrs.iter().any(|attr| {
99                 attr.check_name("doc")
100                     && match attr.meta_item_list() {
101                         None => false,
102                         Some(l) => attr::list_contains_name(&l[..], "hidden"),
103                     }
104             });
105         self.doc_hidden_stack.push(doc_hidden);
106     }
107
108     fn exit_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx [ast::Attribute]) {
109         self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
110     }
111
112     fn check_crate(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx hir::Crate) {
113         self.check_missing_docs_attrs(cx, &krate.attrs, krate.span, "crate");
114     }
115
116     fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item) {
117         let desc = match it.node {
118             hir::ItemKind::Const(..) => "a constant",
119             hir::ItemKind::Enum(..) => "an enum",
120             hir::ItemKind::Fn(..) => {
121                 // ignore main()
122                 if it.ident.name == "main" {
123                     let def_id = cx.tcx.hir().local_def_id(it.id);
124                     let def_key = cx.tcx.hir().def_key(def_id);
125                     if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) {
126                         return;
127                     }
128                 }
129                 "a function"
130             },
131             hir::ItemKind::Mod(..) => "a module",
132             hir::ItemKind::Static(..) => "a static",
133             hir::ItemKind::Struct(..) => "a struct",
134             hir::ItemKind::Trait(..) => "a trait",
135             hir::ItemKind::TraitAlias(..) => "a trait alias",
136             hir::ItemKind::Ty(..) => "a type alias",
137             hir::ItemKind::Union(..) => "a union",
138             hir::ItemKind::Existential(..) => "an existential type",
139             hir::ItemKind::ExternCrate(..)
140             | hir::ItemKind::ForeignMod(..)
141             | hir::ItemKind::GlobalAsm(..)
142             | hir::ItemKind::Impl(..)
143             | hir::ItemKind::Use(..) => return,
144         };
145
146         self.check_missing_docs_attrs(cx, &it.attrs, it.span, desc);
147     }
148
149     fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, trait_item: &'tcx hir::TraitItem) {
150         let desc = match trait_item.node {
151             hir::TraitItemKind::Const(..) => "an associated constant",
152             hir::TraitItemKind::Method(..) => "a trait method",
153             hir::TraitItemKind::Type(..) => "an associated type",
154         };
155
156         self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, desc);
157     }
158
159     fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem) {
160         // If the method is an impl for a trait, don't doc.
161         let def_id = cx.tcx.hir().local_def_id(impl_item.id);
162         match cx.tcx.associated_item(def_id).container {
163             ty::TraitContainer(_) => return,
164             ty::ImplContainer(cid) => {
165                 if cx.tcx.impl_trait_ref(cid).is_some() {
166                     return;
167                 }
168             },
169         }
170
171         let desc = match impl_item.node {
172             hir::ImplItemKind::Const(..) => "an associated constant",
173             hir::ImplItemKind::Method(..) => "a method",
174             hir::ImplItemKind::Type(_) => "an associated type",
175             hir::ImplItemKind::Existential(_) => "an existential type",
176         };
177         self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc);
178     }
179
180     fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, sf: &'tcx hir::StructField) {
181         if !sf.is_positional() {
182             self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a struct field");
183         }
184     }
185
186     fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, v: &'tcx hir::Variant, _: &hir::Generics) {
187         self.check_missing_docs_attrs(cx, &v.node.attrs, v.span, "a variant");
188     }
189 }