DEPRECATED_SEMVER,
USELESS_ATTRIBUTE,
UNKNOWN_CLIPPY_LINTS,
+ BLANKET_CLIPPY_RESTRICTION_LINTS,
]);
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Attributes {
- fn check_attribute(&mut self, cx: &LateContext<'a, 'tcx>, attr: &'tcx Attribute) {
+impl<'tcx> LateLintPass<'tcx> for Attributes {
+ fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
if let Some(items) = &attr.meta_item_list() {
if let Some(ident) = attr.ident() {
- match &*ident.as_str() {
+ let ident = &*ident.as_str();
+ match ident {
"allow" | "warn" | "deny" | "forbid" => {
- check_clippy_lint_names(cx, items);
+ check_clippy_lint_names(cx, ident, items);
},
_ => {},
}
}
}
- #[allow(clippy::single_match_else)]
- fn check_clippy_lint_names(cx: &LateContext<'_>, items: &[NestedMetaItem]) {
- let lint_store = cx.lints();
- for lint in items {
-fn check_clippy_lint_names(cx: &LateContext<'_, '_>, ident: &str, items: &[NestedMetaItem]) {
++fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) {
+ fn extract_name(lint: &NestedMetaItem) -> Option<SymbolStr> {
if_chain! {
if let Some(meta_item) = lint.meta_item();
if meta_item.path.segments.len() > 1;
}
}
-fn lint_deref(cx: &LateContext<'_, '_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) {
+fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) {
match method_name {
"deref" => {
- if cx.tcx.lang_items().deref_trait().map_or(false, |id| {
+ let impls_deref_trait = cx.tcx.lang_items().deref_trait().map_or(false, |id| {
implements_trait(cx, cx.tables().expr_ty(&call_expr), id, &[])
- }) {
+ });
+ if impls_deref_trait {
span_lint_and_sugg(
cx,
EXPLICIT_DEREF_METHODS,
--- /dev/null
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MapIdentity {
- fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
+ use crate::utils::{
+ is_adjusted, is_type_diagnostic_item, match_path, match_trait_method, match_var, paths, remove_blocks,
+ span_lint_and_sugg,
+ };
+ use if_chain::if_chain;
+ use rustc_errors::Applicability;
+ use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
+ use rustc_lint::{LateContext, LateLintPass};
+ use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+ declare_clippy_lint! {
+ /// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
+ ///
+ /// **Why is this bad?** It can be written more concisely without the call to `map`.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// let x = [1, 2, 3];
+ /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let x = [1, 2, 3];
+ /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
+ /// ```
+ pub MAP_IDENTITY,
+ complexity,
+ "using iterator.map(|x| x)"
+ }
+
+ declare_lint_pass!(MapIdentity => [MAP_IDENTITY]);
+
-fn get_map_argument<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> {
++impl<'tcx> LateLintPass<'tcx> for MapIdentity {
++ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ if expr.span.from_expansion() {
+ return;
+ }
+
+ if_chain! {
+ if let Some([caller, func]) = get_map_argument(cx, expr);
+ if is_expr_identity_function(cx, func);
+ then {
+ span_lint_and_sugg(
+ cx,
+ MAP_IDENTITY,
+ expr.span.trim_start(caller.span).unwrap(),
+ "unnecessary map of the identity function",
+ "remove the call to `map`",
+ String::new(),
+ Applicability::MachineApplicable
+ )
+ }
+ }
+ }
+ }
+
+ /// Returns the arguments passed into map() if the expression is a method call to
+ /// map(). Otherwise, returns None.
-fn is_expr_identity_function(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> {
+ if_chain! {
+ if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
+ if args.len() == 2 && method.ident.as_str() == "map";
+ let caller_ty = cx.tables().expr_ty(&args[0]);
+ if match_trait_method(cx, expr, &paths::ITERATOR)
+ || is_type_diagnostic_item(cx, caller_ty, sym!(result_type))
+ || is_type_diagnostic_item(cx, caller_ty, sym!(option_type));
+ then {
+ Some(args)
+ } else {
+ None
+ }
+ }
+ }
+
+ /// Checks if an expression represents the identity function
+ /// Only examines closures and `std::convert::identity`
-fn is_body_identity_function(cx: &LateContext<'_, '_>, func: &Body<'_>) -> bool {
++fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ match expr.kind {
+ ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
+ ExprKind::Path(QPath::Resolved(_, ref path)) => match_path(path, &paths::STD_CONVERT_IDENTITY),
+ _ => false,
+ }
+ }
+
+ /// Checks if a function's body represents the identity function
+ /// Looks for bodies of the form `|x| x`, `|x| return x`, `|x| { return x }` or `|x| {
+ /// return x; }`
-fn match_expr_param(cx: &LateContext<'_, '_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
++fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
+ let params = func.params;
+ let body = remove_blocks(&func.value);
+
+ // if there's less/more than one parameter, then it is not the identity function
+ if params.len() != 1 {
+ return false;
+ }
+
+ match body.kind {
+ ExprKind::Path(QPath::Resolved(None, _)) => match_expr_param(cx, body, params[0].pat),
+ ExprKind::Ret(Some(ref ret_val)) => match_expr_param(cx, ret_val, params[0].pat),
+ ExprKind::Block(ref block, _) => {
+ if_chain! {
+ if block.stmts.len() == 1;
+ if let StmtKind::Semi(ref expr) | StmtKind::Expr(ref expr) = block.stmts[0].kind;
+ if let ExprKind::Ret(Some(ref ret_val)) = expr.kind;
+ then {
+ match_expr_param(cx, ret_val, params[0].pat)
+ } else {
+ false
+ }
+ }
+ },
+ _ => false,
+ }
+ }
+
+ /// Returns true iff an expression returns the same thing as a parameter's pattern
- match_var(expr, ident.name) && !(cx.tables().hir_owner == Some(expr.hir_id.owner) && is_adjusted(cx, expr))
++fn match_expr_param(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
+ if let PatKind::Binding(_, _, ident, _) = pat.kind {
++ match_var(expr, ident.name) && !(cx.tables().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr))
+ } else {
+ false
+ }
+ }
matches!(&walk_ptrs_ty(cx.tables().expr_ty(expr)).kind, ty::Array(_, _))
}
- fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
-fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
++fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
+ #[derive(Default)]
+ struct EqImpl {
+ ty_eq_other: bool,
+ other_eq_ty: bool,
+ }
+
+ impl EqImpl {
+ fn is_implemented(&self) -> bool {
+ self.ty_eq_other || self.other_eq_ty
+ }
+ }
+
- fn symmetric_partial_eq<'tcx>(cx: &LateContext<'_, 'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
++ fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
+ cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
+ ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
+ other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
+ })
+ }
+
let (arg_ty, snip) = match expr.kind {
ExprKind::MethodCall(.., ref args, _) if args.len() == 1 => {
if match_trait_method(cx, expr, &paths::TO_STRING) || match_trait_method(cx, expr, &paths::TO_OWNED) {
}
}
- fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &Ty<'_>) {
-fn check_cast(cx: &LateContext<'_, '_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) {
++fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) {
if_chain! {
if let TyKind::Ptr(ref mut_ty) = ty.kind;
if let ExprKind::Lit(ref lit) = e.kind;
last: Option<HirId>,
}
- impl_lint_pass!(Regex => [INVALID_REGEX, REGEX_MACRO, TRIVIAL_REGEX]);
+ impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Regex {
- fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
+impl<'tcx> LateLintPass<'tcx> for Regex {
- fn check_crate(&mut self, _: &LateContext<'tcx>, _: &'tcx Crate<'_>) {
- self.spans.clear();
- }
-
- fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
- if_chain! {
- if self.last.is_none();
- if let Some(ref expr) = block.expr;
- if match_type(cx, cx.tables().expr_ty(expr), &paths::REGEX);
- if let Some(span) = is_expn_of(expr.span, "regex");
- then {
- if !self.spans.contains(&span) {
- span_lint(
- cx,
- REGEX_MACRO,
- span,
- "`regex!(_)` found. \
- Please use `Regex::new(_)`, which is faster for now."
- );
- self.spans.insert(span);
- }
- self.last = Some(block.hir_id);
- }
- }
- }
-
- fn check_block_post(&mut self, _: &LateContext<'tcx>, block: &'tcx Block<'_>) {
- if self.last.map_or(false, |id| block.hir_id == id) {
- self.last = None;
- }
- }
-
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! {
if let ExprKind::Call(ref fun, ref args) = expr.kind;
if let ExprKind::Path(ref qpath) = fun.kind;