Support free functions in disallowed-methods lint

In other words, support:

`disallowed_methods = ["alloc::vec::Vec::new"]` (a free function) in
addition to
`disallowed_methods = ["alloc::vec::Vec::leak"]` (a method).

Improve the documentation to clarify that users must specify the full
qualified path for each disallowed function, which can be confusing for
reexports. Include an example `clippy.toml`.

Simplify the actual lint pass so we can reuse `utils::fn_def_id`.
This commit is contained in:
Philip Hayes 2021-01-29 22:18:56 -08:00
parent 357c6a7e27
commit 7b7e3ca511
5 changed files with 50 additions and 29 deletions

View file

@ -1,29 +1,47 @@
use crate::utils::span_lint;
use crate::utils::{fn_def_id, span_lint};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{Expr, ExprKind};
use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Symbol;
declare_clippy_lint! {
/// **What it does:** Lints for specific trait methods defined in clippy.toml
/// **What it does:** Denies the configured methods and functions in clippy.toml
///
/// **Why is this bad?** Some methods are undesirable in certain contexts,
/// and it would be beneficial to lint for them as needed.
/// and it's beneficial to lint for them as needed.
///
/// **Known problems:** None.
/// **Known problems:** Currently, you must write each function as a
/// fully-qualified path. 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 `Duration::as_secs`, your clippy.toml
/// configuration would look like
/// `disallowed-methods = ["core::time::Duration::as_secs"]` and not
/// `disallowed-methods = ["std::time::Duration::as_secs"]` as you might expect.
///
/// **Example:**
///
/// ```rust,ignore
/// // example code where clippy issues a warning
/// foo.bad_method(); // Foo::bad_method is disallowed in the configuration
/// An example clippy.toml configuration:
/// ```toml
/// # clippy.toml
/// disallowed-methods = ["alloc::vec::Vec::leak", "std::time::Instant::now"]
/// ```
///
/// ```rust,ignore
/// // Example code where clippy issues a warning
/// let xs = vec![1, 2, 3, 4];
/// xs.leak(); // Vec::leak is disallowed in the config.
///
/// let _now = Instant::now(); // Instant::now is disallowed in the config.
/// ```
///
/// Use instead:
/// ```rust,ignore
/// // example code which does not raise clippy warning
/// goodStruct.bad_method(); // GoodStruct::bad_method is not disallowed
/// // Example code which does not raise clippy warning
/// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.
/// xs.push(123); // Vec::push is _not_ disallowed in the config.
/// ```
pub DISALLOWED_METHOD,
nursery,
@ -50,14 +68,12 @@ impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::MethodCall(_path, _, _args, _) = &expr.kind {
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
let method_call = cx.get_def_path(def_id);
if self.disallowed.contains(&method_call) {
let method = method_call
.iter()
.map(|s| s.to_ident_string())
if let Some(def_id) = fn_def_id(cx, expr) {
let func_path = cx.get_def_path(def_id);
if self.disallowed.contains(&func_path) {
let func_path_string = func_path
.into_iter()
.map(Symbol::to_ident_string)
.collect::<Vec<_>>()
.join("::");
@ -65,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
cx,
DISALLOWED_METHOD,
expr.span,
&format!("use of a disallowed method `{}`", method),
&format!("use of a disallowed method `{}`", func_path_string),
);
}
}

View file

@ -169,7 +169,7 @@ define_Conf! {
(max_fn_params_bools, "max_fn_params_bools": u64, 3),
/// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
(warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
/// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses
/// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths.
(disallowed_methods, "disallowed_methods": Vec<String>, Vec::<String>::new()),
/// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
(unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true),

View file

@ -1 +1 @@
disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match"]
disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match", "regex::re_unicode::Regex::new"]

View file

@ -4,10 +4,9 @@ extern crate regex;
use regex::Regex;
fn main() {
let a = vec![1, 2, 3, 4];
let re = Regex::new(r"ab.*c").unwrap();
re.is_match("abc");
let a = vec![1, 2, 3, 4];
a.iter().sum::<i32>();
}

View file

@ -1,16 +1,22 @@
error: use of a disallowed method `regex::re_unicode::Regex::is_match`
--> $DIR/conf_disallowed_method.rs:10:5
error: use of a disallowed method `regex::re_unicode::Regex::new`
--> $DIR/conf_disallowed_method.rs:7:14
|
LL | re.is_match("abc");
| ^^^^^^^^^^^^^^^^^^
LL | let re = Regex::new(r"ab.*c").unwrap();
| ^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::disallowed-method` implied by `-D warnings`
error: use of a disallowed method `regex::re_unicode::Regex::is_match`
--> $DIR/conf_disallowed_method.rs:8:5
|
LL | re.is_match("abc");
| ^^^^^^^^^^^^^^^^^^
error: use of a disallowed method `core::iter::traits::iterator::Iterator::sum`
--> $DIR/conf_disallowed_method.rs:12:5
--> $DIR/conf_disallowed_method.rs:11:5
|
LL | a.iter().sum::<i32>();
| ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
error: aborting due to 3 previous errors