Skip to content

Commit 914428b

Browse files
committed
Merge branch 'master' into sugg
2 parents f3bacc9 + 4e9ca25 commit 914428b

File tree

9 files changed

+200
-23
lines changed

9 files changed

+200
-23
lines changed

CHANGELOG.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Change Log
22
All notable changes to this project will be documented in this file.
33

4-
## 0.0.78 - TBA
4+
## 0.0.78 - 2016-07-02
5+
* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
56
* New lints: [`wrong_transmute`, `double_neg`]
67
* For compatibility, `cargo clippy` does not defines the `clippy` feature
78
introduced in 0.0.76 anymore
@@ -14,6 +15,7 @@ All notable changes to this project will be documented in this file.
1415
## 0.0.76 — 2016-06-10
1516
* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)*
1617
* `cargo clippy` now automatically defines the `clippy` feature
18+
* New lint: [`not_unsafe_ptr_arg_deref`]
1719

1820
## 0.0.75 — 2016-06-08
1921
* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)*
@@ -219,6 +221,7 @@ All notable changes to this project will be documented in this file.
219221
[`non_ascii_literal`]: https://github.com/Manishearth/rust-clippy/wiki#non_ascii_literal
220222
[`nonminimal_bool`]: https://github.com/Manishearth/rust-clippy/wiki#nonminimal_bool
221223
[`nonsensical_open_options`]: https://github.com/Manishearth/rust-clippy/wiki#nonsensical_open_options
224+
[`not_unsafe_ptr_arg_deref`]: https://github.com/Manishearth/rust-clippy/wiki#not_unsafe_ptr_arg_deref
222225
[`ok_expect`]: https://github.com/Manishearth/rust-clippy/wiki#ok_expect
223226
[`option_map_unwrap_or`]: https://github.com/Manishearth/rust-clippy/wiki#option_map_unwrap_or
224227
[`option_map_unwrap_or_else`]: https://github.com/Manishearth/rust-clippy/wiki#option_map_unwrap_or_else

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "clippy"
3-
version = "0.0.77"
3+
version = "0.0.78"
44
authors = [
55
"Manish Goregaokar <[email protected]>",
66
"Andre Bogus <[email protected]>",
@@ -25,7 +25,7 @@ test = false
2525

2626
[dependencies]
2727
# begin automatic update
28-
clippy_lints = { version = "0.0.77", path = "clippy_lints" }
28+
clippy_lints = { version = "0.0.78", path = "clippy_lints" }
2929
# end automatic update
3030

3131
[dev-dependencies]

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Table of contents:
1717

1818
## Lints
1919

20-
There are 157 lints included in this crate:
20+
There are 158 lints included in this crate:
2121

2222
name | default | meaning
2323
---------------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -115,6 +115,7 @@ name
115115
[non_ascii_literal](https://github.com/Manishearth/rust-clippy/wiki#non_ascii_literal) | allow | using any literal non-ASCII chars in a string literal; suggests using the \\u escape instead
116116
[nonminimal_bool](https://github.com/Manishearth/rust-clippy/wiki#nonminimal_bool) | allow | checks for boolean expressions that can be written more concisely
117117
[nonsensical_open_options](https://github.com/Manishearth/rust-clippy/wiki#nonsensical_open_options) | warn | nonsensical combination of options for opening a file
118+
[not_unsafe_ptr_arg_deref](https://github.com/Manishearth/rust-clippy/wiki#not_unsafe_ptr_arg_deref) | warn | public functions dereferencing raw pointer arguments but not marked `unsafe`
118119
[ok_expect](https://github.com/Manishearth/rust-clippy/wiki#ok_expect) | warn | using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result
119120
[option_map_unwrap_or](https://github.com/Manishearth/rust-clippy/wiki#option_map_unwrap_or) | warn | using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`
120121
[option_map_unwrap_or_else](https://github.com/Manishearth/rust-clippy/wiki#option_map_unwrap_or_else) | warn | using `Option.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `map_or_else(g, f)`

clippy_lints/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "clippy_lints"
33
# begin automatic update
4-
version = "0.0.77"
4+
version = "0.0.78"
55
# end automatic update
66
authors = [
77
"Manish Goregaokar <[email protected]>",

clippy_lints/src/functions.rs

+124-15
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
use rustc::lint::*;
2-
use rustc::hir;
31
use rustc::hir::intravisit;
2+
use rustc::hir;
3+
use rustc::ty;
4+
use rustc::lint::*;
5+
use std::collections::HashSet;
46
use syntax::ast;
57
use syntax::codemap::Span;
6-
use utils::span_lint;
8+
use utils::{span_lint, type_is_unsafe_function};
79

810
/// **What it does:** Check for functions with too many parameters.
911
///
@@ -15,7 +17,7 @@ use utils::span_lint;
1517
///
1618
/// **Example:**
1719
///
18-
/// ```
20+
/// ```rust
1921
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) { .. }
2022
/// ```
2123
declare_lint! {
@@ -24,6 +26,30 @@ declare_lint! {
2426
"functions with too many arguments"
2527
}
2628

29+
/// **What it does:** Check for public functions that dereferences raw pointer arguments but are
30+
/// not marked unsafe.
31+
///
32+
/// **Why is this bad?** The function should probably be marked `unsafe`, since for an arbitrary
33+
/// raw pointer, there is no way of telling for sure if it is valid.
34+
///
35+
/// **Known problems:**
36+
///
37+
/// * It does not check functions recursively so if the pointer is passed to a private non-
38+
/// `unsafe` function which does the dereferencing, the lint won't trigger.
39+
/// * It only checks for arguments whose type are raw pointers, not raw pointers got from an
40+
/// argument in some other way (`fn foo(bar: &[*const u8])` or `some_argument.get_raw_ptr()`).
41+
///
42+
/// **Example:**
43+
///
44+
/// ```rust
45+
/// pub fn foo(x: *const u8) { println!("{}", unsafe { *x }); }
46+
/// ```
47+
declare_lint! {
48+
pub NOT_UNSAFE_PTR_ARG_DEREF,
49+
Warn,
50+
"public functions dereferencing raw pointer arguments but not marked `unsafe`"
51+
}
52+
2753
#[derive(Copy,Clone)]
2854
pub struct Functions {
2955
threshold: u64,
@@ -37,29 +63,41 @@ impl Functions {
3763

3864
impl LintPass for Functions {
3965
fn get_lints(&self) -> LintArray {
40-
lint_array!(TOO_MANY_ARGUMENTS)
66+
lint_array!(TOO_MANY_ARGUMENTS, NOT_UNSAFE_PTR_ARG_DEREF)
4167
}
4268
}
4369

4470
impl LateLintPass for Functions {
45-
fn check_fn(&mut self, cx: &LateContext, _: intravisit::FnKind, decl: &hir::FnDecl, _: &hir::Block, span: Span,
46-
nodeid: ast::NodeId) {
71+
fn check_fn(&mut self, cx: &LateContext, kind: intravisit::FnKind, decl: &hir::FnDecl, block: &hir::Block, span: Span, nodeid: ast::NodeId) {
4772
use rustc::hir::map::Node::*;
4873

49-
if let Some(NodeItem(ref item)) = cx.tcx.map.find(cx.tcx.map.get_parent_node(nodeid)) {
50-
match item.node {
51-
hir::ItemImpl(_, _, _, Some(_), _, _) |
52-
hir::ItemDefaultImpl(..) => return,
53-
_ => (),
54-
}
74+
let is_impl = if let Some(NodeItem(ref item)) = cx.tcx.map.find(cx.tcx.map.get_parent_node(nodeid)) {
75+
matches!(item.node, hir::ItemImpl(_, _, _, Some(_), _, _) | hir::ItemDefaultImpl(..))
76+
} else {
77+
false
78+
};
79+
80+
let unsafety = match kind {
81+
hir::intravisit::FnKind::ItemFn(_, _, unsafety, _, _, _, _) => unsafety,
82+
hir::intravisit::FnKind::Method(_, sig, _, _) => sig.unsafety,
83+
hir::intravisit::FnKind::Closure(_) => return,
84+
};
85+
86+
// don't warn for implementations, it's not their fault
87+
if !is_impl {
88+
self.check_arg_number(cx, decl, span);
5589
}
5690

57-
self.check_arg_number(cx, decl, span);
91+
self.check_raw_ptr(cx, unsafety, decl, block, nodeid);
5892
}
5993

6094
fn check_trait_item(&mut self, cx: &LateContext, item: &hir::TraitItem) {
61-
if let hir::MethodTraitItem(ref sig, _) = item.node {
95+
if let hir::MethodTraitItem(ref sig, ref block) = item.node {
6296
self.check_arg_number(cx, &sig.decl, item.span);
97+
98+
if let Some(ref block) = *block {
99+
self.check_raw_ptr(cx, sig.unsafety, &sig.decl, block, item.id);
100+
}
63101
}
64102
}
65103
}
@@ -74,4 +112,75 @@ impl Functions {
74112
&format!("this function has too many arguments ({}/{})", args, self.threshold));
75113
}
76114
}
115+
116+
fn check_raw_ptr(&self, cx: &LateContext, unsafety: hir::Unsafety, decl: &hir::FnDecl, block: &hir::Block, nodeid: ast::NodeId) {
117+
if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(nodeid) {
118+
let raw_ptrs = decl.inputs.iter().filter_map(|arg| raw_ptr_arg(cx, arg)).collect::<HashSet<_>>();
119+
120+
if !raw_ptrs.is_empty() {
121+
let mut v = DerefVisitor {
122+
cx: cx,
123+
ptrs: raw_ptrs,
124+
};
125+
126+
hir::intravisit::walk_block(&mut v, block);
127+
}
128+
}
129+
}
130+
}
131+
132+
fn raw_ptr_arg(cx: &LateContext, arg: &hir::Arg) -> Option<hir::def_id::DefId> {
133+
if let (&hir::PatKind::Binding(_, _, _), &hir::TyPtr(_)) = (&arg.pat.node, &arg.ty.node) {
134+
cx.tcx.def_map.borrow().get(&arg.pat.id).map(|pr| pr.full_def().def_id())
135+
} else {
136+
None
137+
}
138+
}
139+
140+
struct DerefVisitor<'a, 'tcx: 'a> {
141+
cx: &'a LateContext<'a, 'tcx>,
142+
ptrs: HashSet<hir::def_id::DefId>,
143+
}
144+
145+
impl<'a, 'tcx, 'v> hir::intravisit::Visitor<'v> for DerefVisitor<'a, 'tcx> {
146+
fn visit_expr(&mut self, expr: &'v hir::Expr) {
147+
match expr.node {
148+
hir::ExprCall(ref f, ref args) => {
149+
let ty = self.cx.tcx.expr_ty(f);
150+
151+
if type_is_unsafe_function(ty) {
152+
for arg in args {
153+
self.check_arg(arg);
154+
}
155+
}
156+
}
157+
hir::ExprMethodCall(_, _, ref args) => {
158+
let method_call = ty::MethodCall::expr(expr.id);
159+
let base_type = self.cx.tcx.tables.borrow().method_map[&method_call].ty;
160+
161+
if type_is_unsafe_function(base_type) {
162+
for arg in args {
163+
self.check_arg(arg);
164+
}
165+
}
166+
}
167+
hir::ExprUnary(hir::UnDeref, ref ptr) => self.check_arg(ptr),
168+
_ => (),
169+
}
170+
171+
hir::intravisit::walk_expr(self, expr);
172+
}
173+
}
174+
175+
impl<'a, 'tcx: 'a> DerefVisitor<'a, 'tcx> {
176+
fn check_arg(&self, ptr: &hir::Expr) {
177+
if let Some(def) = self.cx.tcx.def_map.borrow().get(&ptr.id) {
178+
if self.ptrs.contains(&def.full_def().def_id()) {
179+
span_lint(self.cx,
180+
NOT_UNSAFE_PTR_ARG_DEREF,
181+
ptr.span,
182+
"this public function dereferences a raw pointer but is not marked `unsafe`");
183+
}
184+
}
185+
}
77186
}

clippy_lints/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
324324
format::USELESS_FORMAT,
325325
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
326326
formatting::SUSPICIOUS_ELSE_FORMATTING,
327+
functions::NOT_UNSAFE_PTR_ARG_DEREF,
327328
functions::TOO_MANY_ARGUMENTS,
328329
identity_op::IDENTITY_OP,
329330
len_zero::LEN_WITHOUT_IS_EMPTY,

clippy_lints/src/methods.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ declare_lint! {
7373
/// |`is_` |`&self` or none |
7474
/// |`to_` |`&self` |
7575
///
76-
/// **Why is this bad?** Consistency breeds readability. If you follow the conventions, your users won't be surprised that they e.g. need to supply a mutable reference to a `as_..` function.
76+
/// **Why is this bad?** Consistency breeds readability. If you follow the conventions, your users won't be surprised that they, e.g., need to supply a mutable reference to a `as_..` function.
7777
///
7878
/// **Known problems:** None
7979
///

clippy_lints/src/utils/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -714,3 +714,12 @@ pub fn same_tys<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, a: ty::Ty<'tcx>, b: ty::Ty
714714
infcx.can_equate(&new_a, &new_b).is_ok()
715715
})
716716
}
717+
718+
/// Return whether the given type is an `unsafe` function.
719+
pub fn type_is_unsafe_function(ty: ty::Ty) -> bool {
720+
match ty.sty {
721+
ty::TyFnDef(_, _, ref f) |
722+
ty::TyFnPtr(ref f) => f.unsafety == Unsafety::Unsafe,
723+
_ => false,
724+
}
725+
}

tests/compile-fail/functions.rs

+56-2
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,24 @@
33

44
#![deny(clippy)]
55
#![allow(dead_code)]
6+
#![allow(unused_unsafe)]
67

8+
// TOO_MANY_ARGUMENTS
79
fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {}
810

911
fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {
1012
//~^ ERROR: this function has too many arguments (8/7)
1113
}
1214

13-
trait Foo {
15+
pub trait Foo {
1416
fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool);
1517
fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ());
1618
//~^ ERROR: this function has too many arguments (8/7)
19+
20+
fn ptr(p: *const u8);
1721
}
1822

19-
struct Bar;
23+
pub struct Bar;
2024

2125
impl Bar {
2226
fn good_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {}
@@ -28,6 +32,56 @@ impl Bar {
2832
impl Foo for Bar {
2933
fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {}
3034
fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
35+
36+
fn ptr(p: *const u8) {
37+
println!("{}", unsafe { *p });
38+
//~^ ERROR: this public function dereferences a raw pointer but is not marked `unsafe`
39+
println!("{:?}", unsafe { p.as_ref() });
40+
//~^ ERROR: this public function dereferences a raw pointer but is not marked `unsafe`
41+
unsafe { std::ptr::read(p) };
42+
//~^ ERROR: this public function dereferences a raw pointer but is not marked `unsafe`
43+
}
44+
}
45+
46+
// NOT_UNSAFE_PTR_ARG_DEREF
47+
48+
fn private(p: *const u8) {
49+
println!("{}", unsafe { *p });
50+
}
51+
52+
pub fn public(p: *const u8) {
53+
println!("{}", unsafe { *p });
54+
//~^ ERROR: this public function dereferences a raw pointer but is not marked `unsafe`
55+
println!("{:?}", unsafe { p.as_ref() });
56+
//~^ ERROR: this public function dereferences a raw pointer but is not marked `unsafe`
57+
unsafe { std::ptr::read(p) };
58+
//~^ ERROR: this public function dereferences a raw pointer but is not marked `unsafe`
59+
}
60+
61+
impl Bar {
62+
fn private(self, p: *const u8) {
63+
println!("{}", unsafe { *p });
64+
}
65+
66+
pub fn public(self, p: *const u8) {
67+
println!("{}", unsafe { *p });
68+
//~^ ERROR: this public function dereferences a raw pointer but is not marked `unsafe`
69+
println!("{:?}", unsafe { p.as_ref() });
70+
//~^ ERROR: this public function dereferences a raw pointer but is not marked `unsafe`
71+
unsafe { std::ptr::read(p) };
72+
//~^ ERROR: this public function dereferences a raw pointer but is not marked `unsafe`
73+
}
74+
75+
pub fn public_ok(self, p: *const u8) {
76+
if !p.is_null() {
77+
println!("{:p}", p);
78+
}
79+
}
80+
81+
pub unsafe fn public_unsafe(self, p: *const u8) {
82+
println!("{}", unsafe { *p });
83+
println!("{:?}", unsafe { p.as_ref() });
84+
}
3185
}
3286

3387
fn main() {}

0 commit comments

Comments
 (0)