Skip to content

Commit f84e26d

Browse files
committed
Auto merge of #4553 - flip1995:rollup-0chhiy3, r=flip1995
Rollup of 4 pull requests Successful merges: - #4511 (New lint: mem_replace_with_uninit) - #4535 (New lint: Require `# Safety` section in pub unsafe fn docs) - #4539 (Changes cast-lossless to a pedantic lint) - #4544 (#4542 remove machine applicable suggestion) Failed merges: r? @ghost changelog: none
2 parents a5e568b + 25c4f84 commit f84e26d

19 files changed

+328
-68
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -1050,12 +1050,14 @@ Released 2018-09-13
10501050
[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
10511051
[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
10521052
[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
1053+
[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
10531054
[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
10541055
[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
10551056
[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
10561057
[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
10571058
[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
10581059
[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
1060+
[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
10591061
[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
10601062
[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
10611063
[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are 313 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 314 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1212

clippy_lints/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ semver = "0.9.0"
2727
serde = { version = "1.0", features = ["derive"] }
2828
toml = "0.5.3"
2929
unicode-normalization = "0.1"
30-
pulldown-cmark = "0.5.3"
30+
pulldown-cmark = "0.6.0"
3131
url = { version = "2.1.0", features = ["serde"] } # cargo requires serde feat in its url dep
3232
# see https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864
3333
if_chain = "1.0.0"

clippy_lints/src/doc.rs

+83-28
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,40 @@ declare_clippy_lint! {
3434
"presence of `_`, `::` or camel-case outside backticks in documentation"
3535
}
3636

37+
declare_clippy_lint! {
38+
/// **What it does:** Checks for the doc comments of publicly visible
39+
/// unsafe functions and warns if there is no `# Safety` section.
40+
///
41+
/// **Why is this bad?** Unsafe functions should document their safety
42+
/// preconditions, so that users can be sure they are using them safely.
43+
///
44+
/// **Known problems:** None.
45+
///
46+
/// **Examples**:
47+
/// ```rust
48+
///# type Universe = ();
49+
/// /// This function should really be documented
50+
/// pub unsafe fn start_apocalypse(u: &mut Universe) {
51+
/// unimplemented!();
52+
/// }
53+
/// ```
54+
///
55+
/// At least write a line about safety:
56+
///
57+
/// ```rust
58+
///# type Universe = ();
59+
/// /// # Safety
60+
/// ///
61+
/// /// This function should not be called before the horsemen are ready.
62+
/// pub unsafe fn start_apocalypse(u: &mut Universe) {
63+
/// unimplemented!();
64+
/// }
65+
/// ```
66+
pub MISSING_SAFETY_DOC,
67+
style,
68+
"`pub unsafe fn` without `# Safety` docs"
69+
}
70+
3771
#[allow(clippy::module_name_repetitions)]
3872
#[derive(Clone)]
3973
pub struct DocMarkdown {
@@ -46,15 +80,28 @@ impl DocMarkdown {
4680
}
4781
}
4882

49-
impl_lint_pass!(DocMarkdown => [DOC_MARKDOWN]);
83+
impl_lint_pass!(DocMarkdown => [DOC_MARKDOWN, MISSING_SAFETY_DOC]);
5084

5185
impl EarlyLintPass for DocMarkdown {
5286
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) {
5387
check_attrs(cx, &self.valid_idents, &krate.attrs);
5488
}
5589

5690
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
57-
check_attrs(cx, &self.valid_idents, &item.attrs);
91+
if check_attrs(cx, &self.valid_idents, &item.attrs) {
92+
return;
93+
}
94+
// no safety header
95+
if let ast::ItemKind::Fn(_, ref header, ..) = item.node {
96+
if item.vis.node.is_pub() && header.unsafety == ast::Unsafety::Unsafe {
97+
span_lint(
98+
cx,
99+
MISSING_SAFETY_DOC,
100+
item.span,
101+
"unsafe function's docs miss `# Safety` section",
102+
);
103+
}
104+
}
58105
}
59106
}
60107

@@ -115,7 +162,7 @@ pub fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<(
115162
panic!("not a doc-comment: {}", comment);
116163
}
117164

118-
pub fn check_attrs<'a>(cx: &EarlyContext<'_>, valid_idents: &FxHashSet<String>, attrs: &'a [ast::Attribute]) {
165+
pub fn check_attrs<'a>(cx: &EarlyContext<'_>, valid_idents: &FxHashSet<String>, attrs: &'a [ast::Attribute]) -> bool {
119166
let mut doc = String::new();
120167
let mut spans = vec![];
121168

@@ -129,7 +176,7 @@ pub fn check_attrs<'a>(cx: &EarlyContext<'_>, valid_idents: &FxHashSet<String>,
129176
}
130177
} else if attr.check_name(sym!(doc)) {
131178
// ignore mix of sugared and non-sugared doc
132-
return;
179+
return true; // don't trigger the safety check
133180
}
134181
}
135182

@@ -140,57 +187,64 @@ pub fn check_attrs<'a>(cx: &EarlyContext<'_>, valid_idents: &FxHashSet<String>,
140187
current += offset_copy;
141188
}
142189

143-
if !doc.is_empty() {
144-
let parser = pulldown_cmark::Parser::new(&doc).into_offset_iter();
145-
// Iterate over all `Events` and combine consecutive events into one
146-
let events = parser.coalesce(|previous, current| {
147-
use pulldown_cmark::Event::*;
148-
149-
let previous_range = previous.1;
150-
let current_range = current.1;
151-
152-
match (previous.0, current.0) {
153-
(Text(previous), Text(current)) => {
154-
let mut previous = previous.to_string();
155-
previous.push_str(&current);
156-
Ok((Text(previous.into()), previous_range))
157-
},
158-
(previous, current) => Err(((previous, previous_range), (current, current_range))),
159-
}
160-
});
161-
check_doc(cx, valid_idents, events, &spans);
190+
if doc.is_empty() {
191+
return false;
162192
}
193+
194+
let parser = pulldown_cmark::Parser::new(&doc).into_offset_iter();
195+
// Iterate over all `Events` and combine consecutive events into one
196+
let events = parser.coalesce(|previous, current| {
197+
use pulldown_cmark::Event::*;
198+
199+
let previous_range = previous.1;
200+
let current_range = current.1;
201+
202+
match (previous.0, current.0) {
203+
(Text(previous), Text(current)) => {
204+
let mut previous = previous.to_string();
205+
previous.push_str(&current);
206+
Ok((Text(previous.into()), previous_range))
207+
},
208+
(previous, current) => Err(((previous, previous_range), (current, current_range))),
209+
}
210+
});
211+
check_doc(cx, valid_idents, events, &spans)
163212
}
164213

165214
fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
166215
cx: &EarlyContext<'_>,
167216
valid_idents: &FxHashSet<String>,
168217
events: Events,
169218
spans: &[(usize, Span)],
170-
) {
219+
) -> bool {
220+
// true if a safety header was found
171221
use pulldown_cmark::Event::*;
172222
use pulldown_cmark::Tag::*;
173223

224+
let mut safety_header = false;
174225
let mut in_code = false;
175226
let mut in_link = None;
227+
let mut in_heading = false;
176228

177229
for (event, range) in events {
178230
match event {
179231
Start(CodeBlock(_)) => in_code = true,
180232
End(CodeBlock(_)) => in_code = false,
181233
Start(Link(_, url, _)) => in_link = Some(url),
182234
End(Link(..)) => in_link = None,
183-
Start(_tag) | End(_tag) => (), // We don't care about other tags
184-
Html(_html) | InlineHtml(_html) => (), // HTML is weird, just ignore it
185-
SoftBreak | HardBreak | TaskListMarker(_) | Code(_) => (),
235+
Start(Heading(_)) => in_heading = true,
236+
End(Heading(_)) => in_heading = false,
237+
Start(_tag) | End(_tag) => (), // We don't care about other tags
238+
Html(_html) => (), // HTML is weird, just ignore it
239+
SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
186240
FootnoteReference(text) | Text(text) => {
187241
if Some(&text) == in_link.as_ref() {
188242
// Probably a link of the form `<http://example.com>`
189243
// Which are represented as a link to "http://example.com" with
190244
// text "http://example.com" by pulldown-cmark
191245
continue;
192246
}
193-
247+
safety_header |= in_heading && text.trim() == "Safety";
194248
if !in_code {
195249
let index = match spans.binary_search_by(|c| c.0.cmp(&range.start)) {
196250
Ok(o) => o,
@@ -207,6 +261,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
207261
},
208262
}
209263
}
264+
safety_header
210265
}
211266

212267
fn check_text(cx: &EarlyContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {

clippy_lints/src/explicit_write.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ fn write_output_string(write_args: &HirVec<Expr>) -> Option<String> {
140140
if output_args.len() > 0;
141141
if let ExprKind::AddrOf(_, ref output_string_expr) = output_args[0].node;
142142
if let ExprKind::Array(ref string_exprs) = output_string_expr.node;
143-
if string_exprs.len() > 0;
143+
// we only want to provide an automatic suggestion for simple (non-format) strings
144+
if string_exprs.len() == 1;
144145
if let ExprKind::Lit(ref lit) = string_exprs[0].node;
145146
if let LitKind::Str(ref write_output, _) = lit.node;
146147
then {

clippy_lints/src/lib.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
667667
shadow::SHADOW_UNRELATED,
668668
strings::STRING_ADD_ASSIGN,
669669
trait_bounds::TYPE_REPETITION_IN_BOUNDS,
670+
types::CAST_LOSSLESS,
670671
types::CAST_POSSIBLE_TRUNCATION,
671672
types::CAST_POSSIBLE_WRAP,
672673
types::CAST_PRECISION_LOSS,
@@ -708,6 +709,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
708709
copies::IFS_SAME_COND,
709710
copies::IF_SAME_THEN_ELSE,
710711
derive::DERIVE_HASH_XOR_EQ,
712+
doc::MISSING_SAFETY_DOC,
711713
double_comparison::DOUBLE_COMPARISONS,
712714
double_parens::DOUBLE_PARENS,
713715
drop_bounds::DROP_BOUNDS,
@@ -781,6 +783,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
781783
matches::SINGLE_MATCH,
782784
mem_discriminant::MEM_DISCRIMINANT_NON_ENUM,
783785
mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
786+
mem_replace::MEM_REPLACE_WITH_UNINIT,
784787
methods::CHARS_LAST_CMP,
785788
methods::CHARS_NEXT_CMP,
786789
methods::CLONE_DOUBLE_REF,
@@ -890,7 +893,6 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
890893
types::ABSURD_EXTREME_COMPARISONS,
891894
types::BORROWED_BOX,
892895
types::BOX_VEC,
893-
types::CAST_LOSSLESS,
894896
types::CAST_PTR_ALIGNMENT,
895897
types::CAST_REF_TO_MUT,
896898
types::CHAR_LIT_AS_U8,
@@ -929,6 +931,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
929931
block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR,
930932
block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT,
931933
collapsible_if::COLLAPSIBLE_IF,
934+
doc::MISSING_SAFETY_DOC,
932935
enum_variants::ENUM_VARIANT_NAMES,
933936
enum_variants::MODULE_INCEPTION,
934937
eq_op::OP_REF,
@@ -1072,7 +1075,6 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
10721075
transmute::TRANSMUTE_PTR_TO_REF,
10731076
transmute::USELESS_TRANSMUTE,
10741077
types::BORROWED_BOX,
1075-
types::CAST_LOSSLESS,
10761078
types::CHAR_LIT_AS_U8,
10771079
types::OPTION_OPTION,
10781080
types::TYPE_COMPLEXITY,
@@ -1116,6 +1118,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
11161118
loops::REVERSE_RANGE_LOOP,
11171119
loops::WHILE_IMMUTABLE_CONDITION,
11181120
mem_discriminant::MEM_DISCRIMINANT_NON_ENUM,
1121+
mem_replace::MEM_REPLACE_WITH_UNINIT,
11191122
methods::CLONE_DOUBLE_REF,
11201123
methods::INTO_ITER_ON_ARRAY,
11211124
methods::TEMPORARY_CSTRING_AS_PTR,

0 commit comments

Comments
 (0)