Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use avoid-breaking-exported-api configuration in types module #7560

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1840,7 +1840,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box serde_api::SerdeApi);
let vec_box_size_threshold = conf.vec_box_size_threshold;
let type_complexity_threshold = conf.type_complexity_threshold;
store.register_late_pass(move || box types::Types::new(vec_box_size_threshold, type_complexity_threshold));
let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
store.register_late_pass(move || box types::Types::new(
vec_box_size_threshold,
type_complexity_threshold,
avoid_breaking_exported_api,
));
store.register_late_pass(|| box booleans::NonminimalBool);
store.register_late_pass(|| box needless_bitwise_bool::NeedlessBitwiseBool);
store.register_late_pass(|| box eq_op::EqOp);
Expand Down
81 changes: 64 additions & 17 deletions clippy_lints/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ declare_clippy_lint! {
pub struct Types {
vec_box_size_threshold: u64,
type_complexity_threshold: u64,
avoid_breaking_exported_api: bool,
}

impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
Expand All @@ -308,19 +309,31 @@ impl<'tcx> LateLintPass<'tcx> for Types {
false
};

let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(id));

self.check_fn_decl(
cx,
decl,
CheckTyContext {
is_in_trait_impl,
is_exported,
..CheckTyContext::default()
},
);
}

fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
let is_exported = cx.access_levels.is_exported(item.def_id);

match item.kind {
ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(cx, ty, CheckTyContext::default()),
ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(
cx,
ty,
CheckTyContext {
is_exported,
..CheckTyContext::default()
},
),
// functions, enums, structs, impls and traits are covered
_ => (),
}
Expand All @@ -342,15 +355,31 @@ impl<'tcx> LateLintPass<'tcx> for Types {
}

fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
self.check_ty(cx, field.ty, CheckTyContext::default());
let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(field.hir_id));

self.check_ty(
cx,
field.ty,
CheckTyContext {
is_exported,
..CheckTyContext::default()
},
);
}

fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'_>) {
let is_exported = cx.access_levels.is_exported(item.def_id);

let context = CheckTyContext {
is_exported,
..CheckTyContext::default()
};

match item.kind {
TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => {
self.check_ty(cx, ty, CheckTyContext::default());
self.check_ty(cx, ty, context);
},
TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, CheckTyContext::default()),
TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, context),
TraitItemKind::Type(..) => (),
}
}
Expand All @@ -370,10 +399,11 @@ impl<'tcx> LateLintPass<'tcx> for Types {
}

impl Types {
pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64) -> Self {
pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64, avoid_breaking_exported_api: bool) -> Self {
Self {
vec_box_size_threshold,
type_complexity_threshold,
avoid_breaking_exported_api,
}
}

Expand Down Expand Up @@ -410,17 +440,24 @@ impl Types {
let hir_id = hir_ty.hir_id;
let res = cx.qpath_res(qpath, hir_id);
if let Some(def_id) = res.opt_def_id() {
let mut triggered = false;
triggered |= box_vec::check(cx, hir_ty, qpath, def_id);
triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id);
triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id);
triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold);
triggered |= option_option::check(cx, hir_ty, qpath, def_id);
triggered |= linked_list::check(cx, hir_ty, def_id);
triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id);

if triggered {
return;
if self.is_type_change_allowed(context) {
// All lints that are being checked in this block are guarded by
// the `avoid_breaking_exported_api` configuration. When adding a
// new lint, please also add the name to the configuration documentation
// in `clippy_lints::utils::conf.rs`
xFrednet marked this conversation as resolved.
Show resolved Hide resolved

let mut triggered = false;
triggered |= box_vec::check(cx, hir_ty, qpath, def_id);
triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id);
triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id);
triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold);
triggered |= option_option::check(cx, hir_ty, qpath, def_id);
triggered |= linked_list::check(cx, hir_ty, def_id);
triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id);

if triggered {
return;
}
}
}
match *qpath {
Expand Down Expand Up @@ -487,11 +524,21 @@ impl Types {
_ => {},
}
}

/// This function checks if the type is allowed to change in the current context
/// based on the `avoid_breaking_exported_api` configuration
fn is_type_change_allowed(&self, context: CheckTyContext) -> bool {
!(context.is_exported && self.avoid_breaking_exported_api)
}
}

#[allow(clippy::struct_excessive_bools)]
#[derive(Clone, Copy, Default)]
struct CheckTyContext {
is_in_trait_impl: bool,
/// `true` for types on local variables.
is_local: bool,
/// `true` for types that are part of the public API.
is_exported: bool,
is_nested_call: bool,
}
11 changes: 6 additions & 5 deletions clippy_lints/src/types/rc_mutex.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_ty_param_diagnostic_item;
use if_chain::if_chain;
use rustc_hir::{self as hir, def_id::DefId, QPath};
Expand All @@ -11,13 +11,14 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
if_chain! {
if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ;
if let Some(_) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) ;

then{
span_lint(
then {
span_lint_and_help(
cx,
RC_MUTEX,
hir_ty.span,
"found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead",
"usage of `Rc<Mutex<_>>`",
None,
"consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead",
);
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion clippy_lints/src/utils/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ macro_rules! define_Conf {

// N.B., this macro is parsed by util/lintlib.py
define_Conf! {
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION.
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_VEC, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
///
/// Suppress lints whenever the suggested change would cause breakage for other crates.
(avoid_breaking_exported_api: bool = true),
Expand Down
28 changes: 16 additions & 12 deletions tests/ui/box_vec.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#![warn(clippy::all)]
#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
#![allow(clippy::blacklisted_name)]
#![allow(
clippy::boxed_local,
clippy::needless_pass_by_value,
clippy::blacklisted_name,
unused
)]

macro_rules! boxit {
($init:expr, $x:ty) => {
Expand All @@ -11,22 +15,22 @@ macro_rules! boxit {
fn test_macro() {
boxit!(Vec::new(), Vec<u8>);
}
pub fn test(foo: Box<Vec<bool>>) {
println!("{:?}", foo.get(0))
}
fn test(foo: Box<Vec<bool>>) {}

pub fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
// pass if #31 is fixed
foo(vec![1, 2, 3])
}

pub fn test_local_not_linted() {
fn test_local_not_linted() {
let _: Box<Vec<bool>>;
}

fn main() {
test(Box::new(Vec::new()));
test2(Box::new(|v| println!("{:?}", v)));
test_macro();
test_local_not_linted();
// All of these test should be allowed because they are part of the
// public api and `avoid_breaking_exported_api` is `false` by default.
pub fn pub_test(foo: Box<Vec<bool>>) {}
pub fn pub_test_ret() -> Box<Vec<bool>> {
Box::new(Vec::new())
}

fn main() {}
6 changes: 3 additions & 3 deletions tests/ui/box_vec.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error: you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`
--> $DIR/box_vec.rs:14:18
--> $DIR/box_vec.rs:18:14
|
LL | pub fn test(foo: Box<Vec<bool>>) {
| ^^^^^^^^^^^^^^
LL | fn test(foo: Box<Vec<bool>>) {}
| ^^^^^^^^^^^^^^
|
= note: `-D clippy::box-vec` implied by `-D warnings`
= help: `Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation
Expand Down
31 changes: 18 additions & 13 deletions tests/ui/linkedlist.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![feature(associated_type_defaults)]
#![warn(clippy::linkedlist)]
#![allow(dead_code, clippy::needless_pass_by_value)]
#![allow(unused, dead_code, clippy::needless_pass_by_value)]

extern crate alloc;
use alloc::collections::linked_list::LinkedList;
Expand All @@ -20,24 +20,29 @@ impl Foo for LinkedList<u8> {
const BAR: Option<LinkedList<u8>> = None;
}

struct Bar;
pub struct Bar {
priv_linked_list_field: LinkedList<u8>,
pub pub_linked_list_field: LinkedList<u8>,
}
impl Bar {
fn foo(_: LinkedList<u8>) {}
}

pub fn test(my_favourite_linked_list: LinkedList<u8>) {
println!("{:?}", my_favourite_linked_list)
}

pub fn test_ret() -> Option<LinkedList<u8>> {
unimplemented!();
// All of these test should be trigger the lint because they are not
// part of the public api
fn test(my_favorite_linked_list: LinkedList<u8>) {}
fn test_ret() -> Option<LinkedList<u8>> {
None
}

pub fn test_local_not_linted() {
fn test_local_not_linted() {
let _: LinkedList<u8>;
}

fn main() {
test(LinkedList::new());
test_local_not_linted();
// All of these test should be allowed because they are part of the
// public api and `avoid_breaking_exported_api` is `false` by default.
pub fn pub_test(the_most_awesome_linked_list: LinkedList<u8>) {}
pub fn pub_test_ret() -> Option<LinkedList<u8>> {
None
}

fn main() {}
24 changes: 16 additions & 8 deletions tests/ui/linkedlist.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -40,28 +40,36 @@ LL | const BAR: Option<LinkedList<u8>>;
= help: a `VecDeque` might work

error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
--> $DIR/linkedlist.rs:25:15
--> $DIR/linkedlist.rs:24:29
|
LL | priv_linked_list_field: LinkedList<u8>,
| ^^^^^^^^^^^^^^
|
= help: a `VecDeque` might work

error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
--> $DIR/linkedlist.rs:28:15
|
LL | fn foo(_: LinkedList<u8>) {}
| ^^^^^^^^^^^^^^
|
= help: a `VecDeque` might work

error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
--> $DIR/linkedlist.rs:28:39
--> $DIR/linkedlist.rs:33:34
|
LL | pub fn test(my_favourite_linked_list: LinkedList<u8>) {
| ^^^^^^^^^^^^^^
LL | fn test(my_favorite_linked_list: LinkedList<u8>) {}
| ^^^^^^^^^^^^^^
|
= help: a `VecDeque` might work

error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
--> $DIR/linkedlist.rs:32:29
--> $DIR/linkedlist.rs:34:25
|
LL | pub fn test_ret() -> Option<LinkedList<u8>> {
| ^^^^^^^^^^^^^^
LL | fn test_ret() -> Option<LinkedList<u8>> {
| ^^^^^^^^^^^^^^
|
= help: a `VecDeque` might work

error: aborting due to 8 previous errors
error: aborting due to 9 previous errors

32 changes: 17 additions & 15 deletions tests/ui/rc_mutex.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
#![warn(clippy::rc_mutex)]
#![allow(clippy::blacklisted_name)]
#![allow(unused, clippy::blacklisted_name)]

use std::rc::Rc;
use std::sync::Mutex;

pub struct MyStruct {
pub struct MyStructWithPrivItem {
foo: Rc<Mutex<i32>>,
}

pub struct MyStructWithPubItem {
pub foo: Rc<Mutex<i32>>,
}

pub struct SubT<T> {
foo: T,
}
Expand All @@ -17,18 +21,16 @@ pub enum MyEnum {
Two,
}

pub fn test1<T>(foo: Rc<Mutex<T>>) {}

pub fn test2(foo: Rc<Mutex<MyEnum>>) {}
// All of these test should be trigger the lint because they are not
// part of the public api
fn test1<T>(foo: Rc<Mutex<T>>) {}
fn test2(foo: Rc<Mutex<MyEnum>>) {}
fn test3(foo: Rc<Mutex<SubT<usize>>>) {}

pub fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
// All of these test should be allowed because they are part of the
// public api and `avoid_breaking_exported_api` is `false` by default.
pub fn pub_test1<T>(foo: Rc<Mutex<T>>) {}
pub fn pub_test2(foo: Rc<Mutex<MyEnum>>) {}
pub fn pub_test3(foo: Rc<Mutex<SubT<usize>>>) {}

fn main() {
test1(Rc::new(Mutex::new(1)));
test2(Rc::new(Mutex::new(MyEnum::One)));
test3(Rc::new(Mutex::new(SubT { foo: 1 })));

let _my_struct = MyStruct {
foo: Rc::new(Mutex::new(1)),
};
}
fn main() {}
Loading