]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/missing_doc.rs
Update upstream source from tag 'upstream/1.52.1+dfsg1'
[rustc.git] / src / tools / clippy / clippy_lints / src / missing_doc.rs
CommitLineData
f20569fa
XL
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/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415
6//
7
8use crate::utils::span_lint;
9use if_chain::if_chain;
10use rustc_ast::ast::{self, MetaItem, MetaItemKind};
11use rustc_ast::attr;
12use rustc_hir as hir;
13use rustc_lint::{LateContext, LateLintPass, LintContext};
14use rustc_middle::ty;
15use rustc_session::{declare_tool_lint, impl_lint_pass};
16use rustc_span::source_map::Span;
17use rustc_span::sym;
18
19declare_clippy_lint! {
20 /// **What it does:** Warns if there is missing doc for any documentable item
21 /// (public or private).
22 ///
23 /// **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS`
24 /// allowed-by-default lint for
25 /// public members, but has no way to enforce documentation of private items.
26 /// This lint fixes that.
27 ///
28 /// **Known problems:** None.
29 pub MISSING_DOCS_IN_PRIVATE_ITEMS,
30 restriction,
31 "detects missing documentation for public and private members"
32}
33
34pub struct MissingDoc {
35 /// Stack of whether #[doc(hidden)] is set
36 /// at each level which has lint attributes.
37 doc_hidden_stack: Vec<bool>,
38}
39
40impl Default for MissingDoc {
41 #[must_use]
42 fn default() -> Self {
43 Self::new()
44 }
45}
46
47impl MissingDoc {
48 #[must_use]
49 pub fn new() -> Self {
50 Self {
51 doc_hidden_stack: vec![false],
52 }
53 }
54
55 fn doc_hidden(&self) -> bool {
56 *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
57 }
58
59 fn has_include(meta: Option<MetaItem>) -> bool {
60 if_chain! {
61 if let Some(meta) = meta;
62 if let MetaItemKind::List(list) = meta.kind;
63 if let Some(meta) = list.get(0);
64 if let Some(name) = meta.ident();
65 then {
66 name.name == sym::include
67 } else {
68 false
69 }
70 }
71 }
72
73 fn check_missing_docs_attrs(
74 &self,
75 cx: &LateContext<'_>,
76 attrs: &[ast::Attribute],
77 sp: Span,
78 article: &'static str,
79 desc: &'static str,
80 ) {
81 // If we're building a test harness, then warning about
82 // documentation is probably not really relevant right now.
83 if cx.sess().opts.test {
84 return;
85 }
86
87 // `#[doc(hidden)]` disables missing_docs check.
88 if self.doc_hidden() {
89 return;
90 }
91
92 if sp.from_expansion() {
93 return;
94 }
95
96 let has_doc = attrs
97 .iter()
98 .any(|a| a.is_doc_comment() || a.doc_str().is_some() || a.is_value_str() || Self::has_include(a.meta()));
99 if !has_doc {
100 span_lint(
101 cx,
102 MISSING_DOCS_IN_PRIVATE_ITEMS,
103 sp,
104 &format!("missing documentation for {} {}", article, desc),
105 );
106 }
107 }
108}
109
110impl_lint_pass!(MissingDoc => [MISSING_DOCS_IN_PRIVATE_ITEMS]);
111
112impl<'tcx> LateLintPass<'tcx> for MissingDoc {
113 fn enter_lint_attrs(&mut self, _: &LateContext<'tcx>, attrs: &'tcx [ast::Attribute]) {
114 let doc_hidden = self.doc_hidden()
115 || attrs.iter().any(|attr| {
116 attr.has_name(sym::doc)
117 && match attr.meta_item_list() {
118 None => false,
119 Some(l) => attr::list_contains_name(&l[..], sym::hidden),
120 }
121 });
122 self.doc_hidden_stack.push(doc_hidden);
123 }
124
125 fn exit_lint_attrs(&mut self, _: &LateContext<'tcx>, _: &'tcx [ast::Attribute]) {
126 self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
127 }
128
129 fn check_crate(&mut self, cx: &LateContext<'tcx>, krate: &'tcx hir::Crate<'_>) {
130 let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID);
131 self.check_missing_docs_attrs(cx, attrs, krate.item.span, "the", "crate");
132 }
133
134 fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) {
135 match it.kind {
136 hir::ItemKind::Fn(..) => {
137 // ignore main()
138 if it.ident.name == sym::main {
139 let def_key = cx.tcx.hir().def_key(it.def_id);
140 if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) {
141 return;
142 }
143 }
144 },
145 hir::ItemKind::Const(..)
146 | hir::ItemKind::Enum(..)
147 | hir::ItemKind::Mod(..)
148 | hir::ItemKind::Static(..)
149 | hir::ItemKind::Struct(..)
150 | hir::ItemKind::Trait(..)
151 | hir::ItemKind::TraitAlias(..)
152 | hir::ItemKind::TyAlias(..)
153 | hir::ItemKind::Union(..)
154 | hir::ItemKind::OpaqueTy(..) => {},
155 hir::ItemKind::ExternCrate(..)
156 | hir::ItemKind::ForeignMod { .. }
157 | hir::ItemKind::GlobalAsm(..)
158 | hir::ItemKind::Impl { .. }
159 | hir::ItemKind::Use(..) => return,
160 };
161
162 let (article, desc) = cx.tcx.article_and_description(it.def_id.to_def_id());
163
164 let attrs = cx.tcx.hir().attrs(it.hir_id());
165 self.check_missing_docs_attrs(cx, attrs, it.span, article, desc);
166 }
167
168 fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) {
169 let (article, desc) = cx.tcx.article_and_description(trait_item.def_id.to_def_id());
170
171 let attrs = cx.tcx.hir().attrs(trait_item.hir_id());
172 self.check_missing_docs_attrs(cx, attrs, trait_item.span, article, desc);
173 }
174
175 fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
176 // If the method is an impl for a trait, don't doc.
177 match cx.tcx.associated_item(impl_item.def_id).container {
178 ty::TraitContainer(_) => return,
179 ty::ImplContainer(cid) => {
180 if cx.tcx.impl_trait_ref(cid).is_some() {
181 return;
182 }
183 },
184 }
185
186 let (article, desc) = cx.tcx.article_and_description(impl_item.def_id.to_def_id());
187 let attrs = cx.tcx.hir().attrs(impl_item.hir_id());
188 self.check_missing_docs_attrs(cx, attrs, impl_item.span, article, desc);
189 }
190
191 fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) {
192 if !sf.is_positional() {
193 let attrs = cx.tcx.hir().attrs(sf.hir_id);
194 self.check_missing_docs_attrs(cx, attrs, sf.span, "a", "struct field");
195 }
196 }
197
198 fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) {
199 let attrs = cx.tcx.hir().attrs(v.id);
200 self.check_missing_docs_attrs(cx, attrs, v.span, "a", "variant");
201 }
202}