Skip to content

Commit e3f143f

Browse files
committed
account for doc visibility
1 parent 737f0a6 commit e3f143f

File tree

10 files changed

+150
-26
lines changed

10 files changed

+150
-26
lines changed

clippy_dev/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ impl Lint {
5757
lints.filter(|l| l.deprecation.is_none() && !l.is_internal())
5858
}
5959

60-
/// Returns the lints in a HashMap, grouped by the different lint groups
60+
/// Returns the lints in a `HashMap`, grouped by the different lint groups
6161
pub fn by_lint_group(lints: &[Self]) -> HashMap<String, Vec<Self>> {
6262
lints
6363
.iter()

clippy_lints/src/consts.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
324324
vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
325325
}
326326

327-
/// Lookup a possibly constant expression from a ExprKind::Path.
327+
/// Lookup a possibly constant expression from a `ExprKind::Path`.
328328
fn fetch_path(&mut self, qpath: &QPath, id: HirId) -> Option<Constant> {
329329
use rustc::mir::interpret::GlobalId;
330330

clippy_lints/src/doc.rs

+65-13
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use crate::utils::span_lint;
22
use itertools::Itertools;
33
use pulldown_cmark;
4-
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
4+
use rustc::hir;
5+
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
56
use rustc::{declare_tool_lint, impl_lint_pass};
67
use rustc_data_structures::fx::FxHashSet;
78
use std::ops::Range;
8-
use syntax::ast;
9+
use syntax::ast::Attribute;
910
use syntax::source_map::{BytePos, Span};
1011
use syntax_pos::Pos;
1112
use url::Url;
@@ -100,28 +101,78 @@ declare_clippy_lint! {
100101
#[derive(Clone)]
101102
pub struct DocMarkdown {
102103
valid_idents: FxHashSet<String>,
104+
in_trait_impl: bool,
103105
}
104106

105107
impl DocMarkdown {
106108
pub fn new(valid_idents: FxHashSet<String>) -> Self {
107-
Self { valid_idents }
109+
Self {
110+
valid_idents,
111+
in_trait_impl: false,
112+
}
108113
}
109114
}
110115

111116
impl_lint_pass!(DocMarkdown => [DOC_MARKDOWN, MISSING_SAFETY_DOC, NEEDLESS_DOCTEST_MAIN]);
112117

113-
impl EarlyLintPass for DocMarkdown {
114-
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) {
118+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DocMarkdown {
119+
fn check_crate(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx hir::Crate) {
115120
check_attrs(cx, &self.valid_idents, &krate.attrs);
116121
}
117122

118-
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
123+
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) {
124+
if check_attrs(cx, &self.valid_idents, &item.attrs) {
125+
return;
126+
}
127+
// no safety header
128+
match item.kind {
129+
hir::ItemKind::Fn(_, ref header, ..) => {
130+
if cx.access_levels.is_exported(item.hir_id) && header.unsafety == hir::Unsafety::Unsafe {
131+
span_lint(
132+
cx,
133+
MISSING_SAFETY_DOC,
134+
item.span,
135+
"unsafe function's docs miss `# Safety` section",
136+
);
137+
}
138+
},
139+
hir::ItemKind::Impl(_, _, _, _, ref trait_ref, ..) => {
140+
self.in_trait_impl = trait_ref.is_some();
141+
},
142+
_ => {},
143+
}
144+
}
145+
146+
fn check_item_post(&mut self, _cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) {
147+
if let hir::ItemKind::Impl(..) = item.kind {
148+
self.in_trait_impl = false;
149+
}
150+
}
151+
152+
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem) {
119153
if check_attrs(cx, &self.valid_idents, &item.attrs) {
120154
return;
121155
}
122156
// no safety header
123-
if let ast::ItemKind::Fn(_, ref header, ..) = item.kind {
124-
if item.vis.node.is_pub() && header.unsafety == ast::Unsafety::Unsafe {
157+
if let hir::TraitItemKind::Method(ref sig, ..) = item.kind {
158+
if cx.access_levels.is_exported(item.hir_id) && sig.header.unsafety == hir::Unsafety::Unsafe {
159+
span_lint(
160+
cx,
161+
MISSING_SAFETY_DOC,
162+
item.span,
163+
"unsafe function's docs miss `# Safety` section",
164+
);
165+
}
166+
}
167+
}
168+
169+
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem) {
170+
if check_attrs(cx, &self.valid_idents, &item.attrs) || self.in_trait_impl {
171+
return;
172+
}
173+
// no safety header
174+
if let hir::ImplItemKind::Method(ref sig, ..) = item.kind {
175+
if cx.access_levels.is_exported(item.hir_id) && sig.header.unsafety == hir::Unsafety::Unsafe {
125176
span_lint(
126177
cx,
127178
MISSING_SAFETY_DOC,
@@ -190,7 +241,7 @@ pub fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<(
190241
panic!("not a doc-comment: {}", comment);
191242
}
192243

193-
pub fn check_attrs<'a>(cx: &EarlyContext<'_>, valid_idents: &FxHashSet<String>, attrs: &'a [ast::Attribute]) -> bool {
244+
pub fn check_attrs<'a>(cx: &LateContext<'_, '_>, valid_idents: &FxHashSet<String>, attrs: &'a [Attribute]) -> bool {
194245
let mut doc = String::new();
195246
let mut spans = vec![];
196247

@@ -240,7 +291,7 @@ pub fn check_attrs<'a>(cx: &EarlyContext<'_>, valid_idents: &FxHashSet<String>,
240291
}
241292

242293
fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
243-
cx: &EarlyContext<'_>,
294+
cx: &LateContext<'_, '_>,
244295
valid_idents: &FxHashSet<String>,
245296
events: Events,
246297
spans: &[(usize, Span)],
@@ -283,6 +334,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
283334
} else {
284335
// Adjust for the beginning of the current `Event`
285336
let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
337+
286338
check_text(cx, valid_idents, &text, span);
287339
}
288340
},
@@ -291,13 +343,13 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
291343
safety_header
292344
}
293345

294-
fn check_code(cx: &EarlyContext<'_>, text: &str, span: Span) {
346+
fn check_code(cx: &LateContext<'_, '_>, text: &str, span: Span) {
295347
if text.contains("fn main() {") {
296348
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
297349
}
298350
}
299351

300-
fn check_text(cx: &EarlyContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
352+
fn check_text(cx: &LateContext<'_, '_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
301353
for word in text.split(|c: char| c.is_whitespace() || c == '\'') {
302354
// Trim punctuation as in `some comment (see foo::bar).`
303355
// ^^
@@ -320,7 +372,7 @@ fn check_text(cx: &EarlyContext<'_>, valid_idents: &FxHashSet<String>, text: &st
320372
}
321373
}
322374

323-
fn check_word(cx: &EarlyContext<'_>, word: &str, span: Span) {
375+
fn check_word(cx: &LateContext<'_, '_>, word: &str, span: Span) {
324376
/// Checks if a string is camel-case, i.e., contains at least two uppercase
325377
/// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
326378
/// Plurals are also excluded (`IDs` is ok).

clippy_lints/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
533533
conf.blacklisted_names.iter().cloned().collect()
534534
));
535535
reg.register_late_lint_pass(box functions::Functions::new(conf.too_many_arguments_threshold, conf.too_many_lines_threshold));
536-
reg.register_early_lint_pass(box doc::DocMarkdown::new(conf.doc_valid_idents.iter().cloned().collect()));
536+
reg.register_late_lint_pass(box doc::DocMarkdown::new(conf.doc_valid_idents.iter().cloned().collect()));
537537
reg.register_late_lint_pass(box neg_multiply::NegMultiply);
538538
reg.register_early_lint_pass(box unsafe_removed_from_name::UnsafeNameRemoval);
539539
reg.register_late_lint_pass(box mem_discriminant::MemDiscriminant);

clippy_lints/src/methods/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1895,7 +1895,7 @@ fn lint_iter_nth<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr, iter_ar
18951895
}
18961896

18971897
fn lint_get_unwrap<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr, get_args: &'tcx [hir::Expr], is_mut: bool) {
1898-
// Note: we don't want to lint `get_mut().unwrap` for HashMap or BTreeMap,
1898+
// Note: we don't want to lint `get_mut().unwrap` for `HashMap` or `BTreeMap`,
18991899
// because they do not implement `IndexMut`
19001900
let mut applicability = Applicability::MachineApplicable;
19011901
let expr_ty = cx.tables.expr_ty(&get_args[0]);

clippy_lints/src/types.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1940,7 +1940,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidUpcastComparisons {
19401940
declare_clippy_lint! {
19411941
/// **What it does:** Checks for public `impl` or `fn` missing generalization
19421942
/// over different hashers and implicitly defaulting to the default hashing
1943-
/// algorithm (SipHash).
1943+
/// algorithm (`SipHash`).
19441944
///
19451945
/// **Why is this bad?** `HashMap` or `HashSet` with custom hashers cannot be
19461946
/// used with them.
@@ -2118,7 +2118,7 @@ enum ImplicitHasherType<'tcx> {
21182118
}
21192119

21202120
impl<'tcx> ImplicitHasherType<'tcx> {
2121-
/// Checks that `ty` is a target type without a BuildHasher.
2121+
/// Checks that `ty` is a target type without a `BuildHasher`.
21222122
fn new<'a>(cx: &LateContext<'a, 'tcx>, hir_ty: &hir::Ty) -> Option<Self> {
21232123
if let TyKind::Path(QPath::Resolved(None, ref path)) = hir_ty.kind {
21242124
let params: Vec<_> = path

tests/ui/doc_unsafe.rs

+54-4
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,59 @@ unsafe fn you_dont_see_me() {
1717
unimplemented!();
1818
}
1919

20+
mod private_mod {
21+
pub unsafe fn only_crate_wide_accessible() {
22+
unimplemented!();
23+
}
24+
25+
pub unsafe fn republished() {
26+
unimplemented!();
27+
}
28+
}
29+
30+
pub use private_mod::republished;
31+
32+
pub trait UnsafeTrait {
33+
unsafe fn woefully_underdocumented(self);
34+
35+
/// # Safety
36+
unsafe fn at_least_somewhat_documented(self);
37+
}
38+
39+
pub struct Struct;
40+
41+
impl UnsafeTrait for Struct {
42+
unsafe fn woefully_underdocumented(self) {
43+
// all is well
44+
}
45+
46+
unsafe fn at_least_somewhat_documented(self) {
47+
// all is still well
48+
}
49+
}
50+
51+
impl Struct {
52+
pub unsafe fn more_undocumented_unsafe() -> Self {
53+
unimplemented!();
54+
}
55+
56+
/// # Safety
57+
pub unsafe fn somewhat_documented(&self) {
58+
unimplemented!();
59+
}
60+
61+
unsafe fn private(&self) {
62+
unimplemented!();
63+
}
64+
}
65+
66+
#[allow(clippy::let_unit_value)]
2067
fn main() {
21-
you_dont_see_me();
22-
destroy_the_planet();
23-
let mut universe = ();
24-
apocalypse(&mut universe);
68+
unsafe {
69+
you_dont_see_me();
70+
destroy_the_planet();
71+
let mut universe = ();
72+
apocalypse(&mut universe);
73+
private_mod::only_crate_wide_accessible();
74+
}
2575
}

tests/ui/doc_unsafe.stderr

+23-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,27 @@ LL | | }
88
|
99
= note: `-D clippy::missing-safety-doc` implied by `-D warnings`
1010

11-
error: aborting due to previous error
11+
error: unsafe function's docs miss `# Safety` section
12+
--> $DIR/doc_unsafe.rs:25:5
13+
|
14+
LL | / pub unsafe fn republished() {
15+
LL | | unimplemented!();
16+
LL | | }
17+
| |_____^
18+
19+
error: unsafe function's docs miss `# Safety` section
20+
--> $DIR/doc_unsafe.rs:33:5
21+
|
22+
LL | unsafe fn woefully_underdocumented(self);
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
25+
error: unsafe function's docs miss `# Safety` section
26+
--> $DIR/doc_unsafe.rs:52:5
27+
|
28+
LL | / pub unsafe fn more_undocumented_unsafe() -> Self {
29+
LL | | unimplemented!();
30+
LL | | }
31+
| |_____^
32+
33+
error: aborting due to 4 previous errors
1234

tests/ui/functions.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![warn(clippy::all)]
22
#![allow(dead_code)]
3-
#![allow(unused_unsafe)]
3+
#![allow(unused_unsafe, clippy::missing_safety_doc)]
44

55
// TOO_MANY_ARGUMENTS
66
fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {}

tests/ui/new_without_default.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![feature(const_fn)]
2-
#![allow(dead_code)]
2+
#![allow(dead_code, clippy::missing_safety_doc)]
33
#![warn(clippy::new_without_default)]
44

55
pub struct Foo;

0 commit comments

Comments
 (0)