Auto merge of #11111 - Alexendoo:regex-def-paths, r=giraffate

Fix regex lints for regex 1.9.0

regex 1.9.0 was [just released](https://blog.burntsushi.net/regex-internals/), which changes where the types are defined. Instead of updating the definitions to the ones in 1.9.0 this PR uses [`def_path_def_ids`](https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/fn.def_path_def_ids.html) on the canonical paths so that we don't have to worry about third party crate internals

This means that it still works with older regex versions too, and will for any future layout changes. I tested it with 1.8.4 and 1.9.0

changelog: [`INVALID_REGEX`], [`TRIVIAL_REGEX`]: now works with regex 1.9.0
This commit is contained in:
bors 2023-07-06 23:40:33 +00:00
commit dd8e44c5a2
3 changed files with 53 additions and 27 deletions

View file

@ -722,7 +722,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef)); store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef));
store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum));
store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
store.register_late_pass(|_| Box::new(regex::Regex)); store.register_late_pass(|_| Box::<regex::Regex>::default());
let ignore_interior_mutability = conf.ignore_interior_mutability.clone(); let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone()))); store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone())));
store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator));

View file

@ -3,12 +3,12 @@ use std::fmt::Display;
use clippy_utils::consts::{constant, Constant}; use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::{match_def_path, paths}; use clippy_utils::{def_path_def_ids, path_def_id, paths};
use if_chain::if_chain;
use rustc_ast::ast::{LitKind, StrStyle}; use rustc_ast::ast::{LitKind, StrStyle};
use rustc_hir::def_id::DefIdMap;
use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{BytePos, Span}; use rustc_span::source_map::{BytePos, Span};
declare_clippy_lint! { declare_clippy_lint! {
@ -55,26 +55,52 @@ declare_clippy_lint! {
"trivial regular expressions" "trivial regular expressions"
} }
declare_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]); #[derive(Copy, Clone)]
enum RegexKind {
Unicode,
UnicodeSet,
Bytes,
BytesSet,
}
#[derive(Default)]
pub struct Regex {
definitions: DefIdMap<RegexKind>,
}
impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
impl<'tcx> LateLintPass<'tcx> for Regex { impl<'tcx> LateLintPass<'tcx> for Regex {
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
// We don't use `match_def_path` here because that relies on matching the exact path, which changed
// between regex 1.8 and 1.9
//
// `def_path_def_ids` will resolve through re-exports but is relatively heavy, so we only perform
// the operation once and store the results
let mut resolve = |path, kind| {
for id in def_path_def_ids(cx, path) {
self.definitions.insert(id, kind);
}
};
resolve(&paths::REGEX_NEW, RegexKind::Unicode);
resolve(&paths::REGEX_BUILDER_NEW, RegexKind::Unicode);
resolve(&paths::REGEX_SET_NEW, RegexKind::UnicodeSet);
resolve(&paths::REGEX_BYTES_NEW, RegexKind::Bytes);
resolve(&paths::REGEX_BYTES_BUILDER_NEW, RegexKind::Bytes);
resolve(&paths::REGEX_BYTES_SET_NEW, RegexKind::BytesSet);
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! { if let ExprKind::Call(fun, [arg]) = expr.kind
if let ExprKind::Call(fun, [arg]) = expr.kind; && let Some(def_id) = path_def_id(cx, fun)
if let ExprKind::Path(ref qpath) = fun.kind; && let Some(regex_kind) = self.definitions.get(&def_id)
if let Some(def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); {
then { match regex_kind {
if match_def_path(cx, def_id, &paths::REGEX_NEW) || RegexKind::Unicode => check_regex(cx, arg, true),
match_def_path(cx, def_id, &paths::REGEX_BUILDER_NEW) { RegexKind::UnicodeSet => check_set(cx, arg, true),
check_regex(cx, arg, true); RegexKind::Bytes => check_regex(cx, arg, false),
} else if match_def_path(cx, def_id, &paths::REGEX_BYTES_NEW) || RegexKind::BytesSet => check_set(cx, arg, false),
match_def_path(cx, def_id, &paths::REGEX_BYTES_BUILDER_NEW) {
check_regex(cx, arg, false);
} else if match_def_path(cx, def_id, &paths::REGEX_SET_NEW) {
check_set(cx, arg, true);
} else if match_def_path(cx, def_id, &paths::REGEX_BYTES_SET_NEW) {
check_set(cx, arg, false);
}
} }
} }
} }

View file

@ -94,12 +94,12 @@ pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; pub const REGEX_BUILDER_NEW: [&str; 3] = ["regex", "RegexBuilder", "new"];
pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; pub const REGEX_BYTES_BUILDER_NEW: [&str; 4] = ["regex", "bytes", "RegexBuilder", "new"];
pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "bytes", "Regex", "new"];
pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; pub const REGEX_BYTES_SET_NEW: [&str; 4] = ["regex", "bytes", "RegexSet", "new"];
pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; pub const REGEX_NEW: [&str; 3] = ["regex", "Regex", "new"];
pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; pub const REGEX_SET_NEW: [&str; 3] = ["regex", "RegexSet", "new"];
pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"]; pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];