Auto merge of #7315 - DevinR528:disallowed-ty, r=giraffate

Add disallowed_type lint, this adds a field to the conf struct

Fixes #7277

changelog: Add ``[`disallowed_type`]`` a lint that can enforce banning types specified in the config.
This commit is contained in:
bors 2021-06-10 00:30:36 +00:00
commit c4636abe72
8 changed files with 263 additions and 1 deletions

View File

@ -2358,6 +2358,7 @@ Released 2018-09-13
[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons

View File

@ -0,0 +1,122 @@
use clippy_utils::diagnostics::span_lint;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{def::Res, Item, ItemKind, PolyTraitRef, TraitBoundModifier, Ty, TyKind, UseKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{Span, Symbol};
declare_clippy_lint! {
/// **What it does:** Denies the configured types in clippy.toml.
///
/// **Why is this bad?** Some types are undesirable in certain contexts.
///
/// **Known problems:** The fully qualified path must be used. This lint
/// doesn't support aliases or reexported names; be aware that many types
/// in `std` are actually reexports.
///
/// For example, if you want to disallow `BTreeMap`, your clippy.toml
/// configuration would look like
/// `disallowed-methods = ["alloc::collections::btree::map::BTreeMap"]` and not
/// `disallowed-methods = ["std::collections::BTreeMap"]` as you might expect.
///
/// N.B. There is no way to ban primitive types.
///
/// **Example:**
///
/// An example clippy.toml configuration:
/// ```toml
/// # clippy.toml
/// disallowed-methods = ["alloc::collections::btree::map::BTreeMap"]
/// ```
///
/// ```rust,ignore
/// use std::collections::BTreeMap;
/// // or its use
/// let x = std::collections::BTreeMap::new();
/// ```
/// Use instead:
/// ```rust,ignore
/// // A similar type that is allowed by the config
/// use std::collections::HashMap;
/// ```
pub DISALLOWED_TYPE,
nursery,
"use of a disallowed type"
}
#[derive(Clone, Debug)]
pub struct DisallowedType {
disallowed: FxHashSet<Vec<Symbol>>,
}
impl DisallowedType {
pub fn new(disallowed: &FxHashSet<String>) -> Self {
Self {
disallowed: disallowed
.iter()
.map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
.collect(),
}
}
}
impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]);
impl<'tcx> LateLintPass<'tcx> for DisallowedType {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if_chain! {
if let ItemKind::Use(path, UseKind::Single) = &item.kind;
if let Res::Def(_, id) = path.res;
let use_path = cx.get_def_path(id);
if let Some(name) = self.disallowed.iter().find(|path| **path == use_path);
then {
emit(cx, name, item.span,);
}
}
}
fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
if_chain! {
if let TyKind::Path(path) = &ty.kind;
if let Some(did) = cx.qpath_res(path, ty.hir_id).opt_def_id();
let use_path = cx.get_def_path(did);
if let Some(name) = self.disallowed.iter().find(|path| **path == use_path);
then {
emit(cx, name, path.span());
}
}
}
fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) {
if_chain! {
if let Res::Def(_, did) = poly.trait_ref.path.res;
let use_path = cx.get_def_path(did);
if let Some(name) = self.disallowed.iter().find(|path| **path == use_path);
then {
emit(cx, name, poly.trait_ref.path.span);
}
}
}
// TODO: if non primitive const generics are a thing
// fn check_generic_arg(&mut self, cx: &LateContext<'tcx>, arg: &'tcx GenericArg<'tcx>) {
// match arg {
// GenericArg::Const(c) => {},
// }
// }
// fn check_generic_param(&mut self, cx: &LateContext<'tcx>, param: &'tcx GenericParam<'tcx>) {
// match param.kind {
// GenericParamKind::Const { .. } => {},
// }
// }
}
fn emit(cx: &LateContext<'_>, name: &[Symbol], span: Span) {
let name = name.iter().map(|s| s.to_ident_string()).collect::<Vec<_>>().join("::");
span_lint(
cx,
DISALLOWED_TYPE,
span,
&format!("`{}` is not allowed according to config", name),
);
}

View File

@ -187,6 +187,7 @@ mod default_numeric_fallback;
mod dereference;
mod derive;
mod disallowed_method;
mod disallowed_type;
mod doc;
mod double_comparison;
mod double_parens;
@ -582,6 +583,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
derive::EXPL_IMPL_CLONE_ON_COPY,
derive::UNSAFE_DERIVE_DESERIALIZE,
disallowed_method::DISALLOWED_METHOD,
disallowed_type::DISALLOWED_TYPE,
doc::DOC_MARKDOWN,
doc::MISSING_ERRORS_DOC,
doc::MISSING_PANICS_DOC,
@ -1760,6 +1762,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
LintId::of(disallowed_method::DISALLOWED_METHOD),
LintId::of(disallowed_type::DISALLOWED_TYPE),
LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
@ -2064,6 +2067,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv));
store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison);
store.register_late_pass(|| box unused_async::UnusedAsync);
let disallowed_types = conf.disallowed_types.iter().cloned().collect::<FxHashSet<_>>();
store.register_late_pass(move || box disallowed_type::DisallowedType::new(&disallowed_types));
}

View File

@ -190,6 +190,8 @@ define_Conf! {
(warn_on_all_wildcard_imports: bool = false),
/// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths.
(disallowed_methods: Vec<String> = Vec::new()),
/// Lint: DISALLOWED_TYPE. The list of disallowed types, written as fully qualified paths.
(disallowed_types: Vec<String> = Vec::new()),
/// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
(unreadable_literal_lint_fractions: bool = true),
/// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other

View File

@ -0,0 +1,9 @@
disallowed-types = [
"std::collections::hash::map::HashMap",
"core::sync::atomic::AtomicU32",
"syn::ty::TypePath",
"proc_macro2::Ident",
"std::thread::Thread",
"std::time::Instant",
"std::io::Read",
]

View File

@ -0,0 +1,35 @@
#![warn(clippy::disallowed_type)]
extern crate quote;
extern crate syn;
use std::sync as foo;
use std::sync::atomic::AtomicU32;
use std::time::Instant as Sneaky;
struct HashMap;
fn bad_return_type() -> fn() -> Sneaky {
todo!()
}
fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {
todo!()
}
fn trait_obj(_: &dyn std::io::Read) {
todo!()
}
static BAD: foo::atomic::AtomicPtr<()> = foo::atomic::AtomicPtr::new(std::ptr::null_mut());
#[allow(clippy::diverging_sub_expression)]
fn main() {
let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
let _ = Sneaky::now();
let _ = foo::atomic::AtomicU32::new(0);
static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
let _ = syn::Ident::new("", todo!());
let _ = HashMap;
}

View File

@ -0,0 +1,88 @@
error: `core::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:7:1
|
LL | use std::sync::atomic::AtomicU32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::disallowed-type` implied by `-D warnings`
error: `std::time::Instant` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:8:1
|
LL | use std::time::Instant as Sneaky;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `std::time::Instant` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:12:33
|
LL | fn bad_return_type() -> fn() -> Sneaky {
| ^^^^^^
error: `std::time::Instant` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:16:28
|
LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {
| ^^^^^^
error: `core::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:16:39
|
LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {
| ^^^^^^^^^^^^^^^^^^^^^^
error: `std::io::Read` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:20:22
|
LL | fn trait_obj(_: &dyn std::io::Read) {
| ^^^^^^^^^^^^^
error: `std::collections::hash::map::HashMap` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:28:48
|
LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: `std::collections::hash::map::HashMap` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:28:12
|
LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `std::time::Instant` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:29:13
|
LL | let _ = Sneaky::now();
| ^^^^^^
error: `core::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:30:13
|
LL | let _ = foo::atomic::AtomicU32::new(0);
| ^^^^^^^^^^^^^^^^^^^^^^
error: `core::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:31:17
|
LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `core::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:31:48
|
LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
| ^^^^^^^^^^^^^^^^^^^^^^
error: `syn::ty::TypePath` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:32:43
|
LL | let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
| ^^^^^^^^^^^^^
error: `proc_macro2::Ident` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:33:13
|
LL | let _ = syn::Ident::new("", todo!());
| ^^^^^^^^^^
error: aborting due to 14 previous errors

View File

@ -1,4 +1,4 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1
error: aborting due to previous error