Skip to content

Commit f1b3615

Browse files
committed
Don't lint missing_docs_in_private_items on proc-macro output
1 parent 3b43bba commit f1b3615

12 files changed

+214
-58
lines changed

clippy_lints/src/missing_doc.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use clippy_utils::attrs::is_doc_hidden;
99
use clippy_utils::diagnostics::span_lint;
10+
use clippy_utils::is_from_proc_macro;
1011
use if_chain::if_chain;
1112
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
1213
use rustc_hir as hir;
@@ -158,14 +159,18 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
158159
let (article, desc) = cx.tcx.article_and_description(it.def_id.to_def_id());
159160

160161
let attrs = cx.tcx.hir().attrs(it.hir_id());
161-
self.check_missing_docs_attrs(cx, attrs, it.span, article, desc);
162+
if !is_from_proc_macro(cx, it) {
163+
self.check_missing_docs_attrs(cx, attrs, it.span, article, desc);
164+
}
162165
}
163166

164167
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) {
165168
let (article, desc) = cx.tcx.article_and_description(trait_item.def_id.to_def_id());
166169

167170
let attrs = cx.tcx.hir().attrs(trait_item.hir_id());
168-
self.check_missing_docs_attrs(cx, attrs, trait_item.span, article, desc);
171+
if !is_from_proc_macro(cx, trait_item) {
172+
self.check_missing_docs_attrs(cx, attrs, trait_item.span, article, desc);
173+
}
169174
}
170175

171176
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
@@ -181,18 +186,24 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
181186

182187
let (article, desc) = cx.tcx.article_and_description(impl_item.def_id.to_def_id());
183188
let attrs = cx.tcx.hir().attrs(impl_item.hir_id());
184-
self.check_missing_docs_attrs(cx, attrs, impl_item.span, article, desc);
189+
if !is_from_proc_macro(cx, impl_item) {
190+
self.check_missing_docs_attrs(cx, attrs, impl_item.span, article, desc);
191+
}
185192
}
186193

187194
fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) {
188195
if !sf.is_positional() {
189196
let attrs = cx.tcx.hir().attrs(sf.hir_id);
190-
self.check_missing_docs_attrs(cx, attrs, sf.span, "a", "struct field");
197+
if !is_from_proc_macro(cx, sf) {
198+
self.check_missing_docs_attrs(cx, attrs, sf.span, "a", "struct field");
199+
}
191200
}
192201
}
193202

194203
fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) {
195204
let attrs = cx.tcx.hir().attrs(v.id);
196-
self.check_missing_docs_attrs(cx, attrs, v.span, "a", "variant");
205+
if !is_from_proc_macro(cx, v) {
206+
self.check_missing_docs_attrs(cx, attrs, v.span, "a", "variant");
207+
}
197208
}
198209
}

clippy_lints/src/unit_types/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,12 @@ declare_clippy_lint! {
9898

9999
declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]);
100100

101-
impl LateLintPass<'_> for UnitTypes {
102-
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
101+
impl<'tcx> LateLintPass<'tcx> for UnitTypes {
102+
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) {
103103
let_unit_value::check(cx, stmt);
104104
}
105105

106-
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
106+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
107107
unit_cmp::check(cx, expr);
108108
unit_arg::check(cx, expr);
109109
}

clippy_lints/src/unit_types/unit_arg.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
2-
use clippy_utils::is_expr_from_proc_macro;
2+
use clippy_utils::is_from_proc_macro;
33
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
44
use if_chain::if_chain;
55
use rustc_errors::Applicability;
@@ -8,7 +8,7 @@ use rustc_lint::LateContext;
88

99
use super::{utils, UNIT_ARG};
1010

11-
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
11+
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
1212
if expr.span.from_expansion() {
1313
return;
1414
}
@@ -45,7 +45,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
4545
}
4646
})
4747
.collect::<Vec<_>>();
48-
if !args_to_recover.is_empty() && !is_expr_from_proc_macro(cx, expr) {
48+
if !args_to_recover.is_empty() && !is_from_proc_macro(cx, expr) {
4949
lint_unit_args(cx, expr, &args_to_recover);
5050
}
5151
},

clippy_utils/src/check_proc_macro.rs

+123-6
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,20 @@
1414
1515
use rustc_ast::ast::{IntTy, LitIntType, LitKind, StrStyle, UintTy};
1616
use rustc_hir::{
17-
Block, BlockCheckMode, Destination, Expr, ExprKind, LoopSource, MatchSource, QPath, UnOp, UnsafeSource, YieldSource,
17+
Block, BlockCheckMode, Destination, Expr, ExprKind, FieldDef, FnHeader, Impl, ImplItem, ImplItemKind, IsAuto, Item,
18+
ItemKind, LoopSource, MatchSource, QPath, TraitItem, TraitItemKind, UnOp, UnsafeSource, Unsafety, Variant,
19+
VariantData, VisibilityKind, YieldSource,
1820
};
1921
use rustc_lint::{LateContext, LintContext};
2022
use rustc_middle::ty::TyCtxt;
2123
use rustc_session::Session;
2224
use rustc_span::{Span, Symbol};
25+
use rustc_target::spec::abi::Abi;
2326

2427
#[derive(Clone, Copy)]
25-
enum Pat {
28+
pub enum Pat {
2629
Str(&'static str),
30+
MultiStr(&'static [&'static str]),
2731
Sym(Symbol),
2832
Num,
2933
}
@@ -42,10 +46,12 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) ->
4246
let end_str = s.trim_end_matches(|c: char| c.is_whitespace() || c == ')' || c == ',');
4347
(match start_pat {
4448
Pat::Str(text) => start_str.starts_with(text),
49+
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
4550
Pat::Sym(sym) => start_str.starts_with(sym.as_str()),
4651
Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit),
4752
} && match end_pat {
4853
Pat::Str(text) => end_str.ends_with(text),
54+
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.ends_with(s)),
4955
Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
5056
Pat::Num => end_str.as_bytes().last().map_or(false, u8::is_ascii_hexdigit),
5157
})
@@ -154,10 +160,121 @@ fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
154160
}
155161
}
156162

157-
/// Checks if the expression likely came from a proc-macro
158-
pub fn is_expr_from_proc_macro(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
159-
let (start_pat, end_pat) = expr_search_pat(cx.tcx, e);
160-
!span_matches_pat(cx.sess(), e.span, start_pat, end_pat)
163+
fn fn_header_search_pat(header: FnHeader) -> Pat {
164+
if header.is_async() {
165+
Pat::Str("async")
166+
} else if header.is_const() {
167+
Pat::Str("const")
168+
} else if header.is_unsafe() {
169+
Pat::Str("unsafe")
170+
} else if header.abi != Abi::Rust {
171+
Pat::Str("extern")
172+
} else {
173+
Pat::MultiStr(&["fn", "extern"])
174+
}
175+
}
176+
177+
fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) {
178+
let (start_pat, end_pat) = match &item.kind {
179+
ItemKind::ExternCrate(_) => (Pat::Str("extern"), Pat::Str(";")),
180+
ItemKind::Static(..) => (Pat::Str("static"), Pat::Str(";")),
181+
ItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
182+
ItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
183+
ItemKind::ForeignMod { .. } => (Pat::Str("extern"), Pat::Str("}")),
184+
ItemKind::TyAlias(..) | ItemKind::OpaqueTy(_) => (Pat::Str("type"), Pat::Str(";")),
185+
ItemKind::Enum(..) => (Pat::Str("enum"), Pat::Str("}")),
186+
ItemKind::Struct(VariantData::Struct(..), _) => (Pat::Str("struct"), Pat::Str("}")),
187+
ItemKind::Struct(..) => (Pat::Str("struct"), Pat::Str(";")),
188+
ItemKind::Union(..) => (Pat::Str("union"), Pat::Str("}")),
189+
ItemKind::Trait(_, Unsafety::Unsafe, ..)
190+
| ItemKind::Impl(Impl {
191+
unsafety: Unsafety::Unsafe,
192+
..
193+
}) => (Pat::Str("unsafe"), Pat::Str("}")),
194+
ItemKind::Trait(IsAuto::Yes, ..) => (Pat::Str("auto"), Pat::Str("}")),
195+
ItemKind::Trait(..) => (Pat::Str("trait"), Pat::Str("}")),
196+
ItemKind::Impl(_) => (Pat::Str("impl"), Pat::Str("}")),
197+
_ => return (Pat::Str(""), Pat::Str("")),
198+
};
199+
if matches!(item.vis.node, VisibilityKind::Inherited) {
200+
(start_pat, end_pat)
201+
} else {
202+
(Pat::Str("pub"), end_pat)
203+
}
204+
}
205+
206+
fn trait_item_search_pat(item: &TraitItem<'_>) -> (Pat, Pat) {
207+
match &item.kind {
208+
TraitItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
209+
TraitItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")),
210+
TraitItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
211+
}
212+
}
213+
214+
fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) {
215+
let (start_pat, end_pat) = match &item.kind {
216+
ImplItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
217+
ImplItemKind::TyAlias(..) => (Pat::Str("type"), Pat::Str(";")),
218+
ImplItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
219+
};
220+
if matches!(item.vis.node, VisibilityKind::Inherited) {
221+
(start_pat, end_pat)
222+
} else {
223+
(Pat::Str("pub"), end_pat)
224+
}
225+
}
226+
227+
fn field_def_search_pat(def: &FieldDef<'_>) -> (Pat, Pat) {
228+
if matches!(def.vis.node, VisibilityKind::Inherited) {
229+
if def.is_positional() {
230+
(Pat::Str(""), Pat::Str(""))
231+
} else {
232+
(Pat::Sym(def.ident.name), Pat::Str(""))
233+
}
234+
} else {
235+
(Pat::Str("pub"), Pat::Str(""))
236+
}
237+
}
238+
239+
fn variant_search_pat(v: &Variant<'_>) -> (Pat, Pat) {
240+
match v.data {
241+
VariantData::Struct(..) => (Pat::Sym(v.ident.name), Pat::Str("}")),
242+
VariantData::Tuple(..) => (Pat::Sym(v.ident.name), Pat::Str("")),
243+
VariantData::Unit(..) => (Pat::Sym(v.ident.name), Pat::Sym(v.ident.name)),
244+
}
245+
}
246+
247+
pub trait WithSearchPat {
248+
type Context: LintContext;
249+
fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat);
250+
fn span(&self) -> Span;
251+
}
252+
macro_rules! impl_with_search_pat {
253+
($cx:ident: $ty:ident with $fn:ident $(($tcx:ident))?) => {
254+
impl<'cx> WithSearchPat for $ty<'cx> {
255+
type Context = $cx<'cx>;
256+
#[allow(unused_variables)]
257+
fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat) {
258+
$(let $tcx = cx.tcx;)?
259+
$fn($($tcx,)? self)
260+
}
261+
fn span(&self) -> Span {
262+
self.span
263+
}
264+
}
265+
};
266+
}
267+
impl_with_search_pat!(LateContext: Expr with expr_search_pat(tcx));
268+
impl_with_search_pat!(LateContext: Item with item_search_pat);
269+
impl_with_search_pat!(LateContext: TraitItem with trait_item_search_pat);
270+
impl_with_search_pat!(LateContext: ImplItem with impl_item_search_pat);
271+
impl_with_search_pat!(LateContext: FieldDef with field_def_search_pat);
272+
impl_with_search_pat!(LateContext: Variant with variant_search_pat);
273+
274+
/// Checks if the item likely came from a proc-macro
275+
pub fn is_from_proc_macro<T: WithSearchPat>(cx: &T::Context, item: &T) -> bool {
276+
let (start_pat, end_pat) = item.search_pat(cx);
277+
!span_matches_pat(cx.sess(), item.span(), start_pat, end_pat)
161278
}
162279

163280
/// Checks if the span actually refers to a match expression

clippy_utils/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ pub mod usage;
5959
pub mod visitors;
6060

6161
pub use self::attrs::*;
62-
pub use self::check_proc_macro::{is_expr_from_proc_macro, is_span_if, is_span_match};
62+
pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
6363
pub use self::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, SpanlessHash};
6464

6565
use std::collections::hash_map::Entry;

tests/ui/missing-doc.rs tests/ui/missing_doc.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
// aux-build: proc_macro_with_span.rs
2+
13
#![warn(clippy::missing_docs_in_private_items)]
24
// When denying at the crate level, be sure to not get random warnings from the
35
// injected intrinsics by the compiler.
46
#![allow(dead_code)]
57
//! Some garbage docs for the crate here
68
#![doc = "More garbage"]
79

10+
extern crate proc_macro_with_span;
11+
12+
use proc_macro_with_span::with_span;
813
use std::arch::global_asm;
914

1015
type Typedef = String;
@@ -100,3 +105,11 @@ fn main() {}
100105

101106
// Ensure global asm doesn't require documentation.
102107
global_asm! { "" }
108+
109+
// Don't lint proc macro output with an unexpected span.
110+
with_span!(span pub struct FooPm { pub field: u32});
111+
with_span!(span pub struct FooPm2;);
112+
with_span!(span pub enum FooPm3 { A, B(u32), C { field: u32 }});
113+
with_span!(span pub fn foo_pm() {});
114+
with_span!(span pub static FOO_PM: u32 = 0;);
115+
with_span!(span pub const FOO2_PM: u32 = 0;);

0 commit comments

Comments
 (0)