Skip to content

Commit e3ad953

Browse files
committed
fix(double_parens): don't lint in proc-macros
1 parent 9f2f30f commit e3ad953

File tree

2 files changed

+181
-3
lines changed

2 files changed

+181
-3
lines changed

clippy_lints/src/double_parens.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::is_from_proc_macro;
23
use clippy_utils::source::{HasSession, snippet_with_applicability, snippet_with_context};
34
use rustc_ast::ast::{Expr, ExprKind, MethodCall};
45
use rustc_errors::Applicability;
@@ -48,7 +49,7 @@ impl EarlyLintPass for DoubleParens {
4849
// ^^^^ inner
4950
ExprKind::Paren(inner) if matches!(inner.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => {
5051
// suggest removing the outer parens
51-
if expr.span.eq_ctxt(inner.span) {
52+
if expr.span.eq_ctxt(inner.span) && !is_from_proc_macro(cx, &**inner) {
5253
let mut applicability = Applicability::MachineApplicable;
5354
// We don't need to use `snippet_with_context` here, because:
5455
// - if `inner`'s `ctxt` is from macro, we don't lint in the first place (see the check above)
@@ -75,7 +76,7 @@ impl EarlyLintPass for DoubleParens {
7576
&& let ExprKind::Paren(inner) = &arg.kind =>
7677
{
7778
// suggest removing the inner parens
78-
if expr.span.eq_ctxt(arg.span) {
79+
if expr.span.eq_ctxt(arg.span) && !is_from_proc_macro(cx, &**arg) {
7980
let mut applicability = Applicability::MachineApplicable;
8081
let sugg = snippet_with_context(cx.sess(), inner.span, arg.span.ctxt(), "_", &mut applicability).0;
8182
span_lint_and_sugg(

clippy_utils/src/check_proc_macro.rs

Lines changed: 178 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ use rustc_abi::ExternAbi;
1616
use rustc_ast as ast;
1717
use rustc_ast::AttrStyle;
1818
use rustc_ast::ast::{
19-
AttrKind, Attribute, GenericArgs, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy,
19+
AttrKind, Attribute, GenericArgs, IntTy, LitIntType, LitKind, RangeLimits, StrStyle, StructExpr, TraitObjectSyntax,
20+
UintTy,
2021
};
2122
use rustc_ast::token::CommentKind;
2223
use rustc_hir::intravisit::FnKind;
@@ -419,6 +420,22 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
419420
}
420421
}
421422

423+
fn ast_path_search_pat(path: &ast::Path) -> (Pat, Pat) {
424+
let (head, tail) = match &*path.segments {
425+
[] => return (Pat::Str(""), Pat::Str("")),
426+
[p] => (Pat::Sym(p.ident.name), p),
427+
[p, .., tail] => (Pat::Sym(p.ident.name), tail),
428+
};
429+
(
430+
head,
431+
if tail.args.is_some() {
432+
Pat::Str(">")
433+
} else {
434+
Pat::Sym(tail.ident.name)
435+
},
436+
)
437+
}
438+
422439
fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) {
423440
use ast::{Extern, FnRetTy, MutTy, Safety, TraitObjectSyntax, TyKind};
424441

@@ -537,6 +554,165 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) {
537554
}
538555
}
539556

557+
/// Get the search patterns to use for the given literal
558+
fn token_lit_search_pat(lit: &ast::token::Lit) -> (Pat, Pat) {
559+
use ast::token::LitKind;
560+
561+
match lit.kind {
562+
LitKind::Bool => (Pat::MultiStr(&["true", "false"]), Pat::MultiStr(&["true", "false"])),
563+
LitKind::Byte => (Pat::Str("b'"), Pat::Str("'")),
564+
LitKind::ByteStr => (Pat::Str("b\""), Pat::Str("\"")),
565+
LitKind::ByteStrRaw(0) => (Pat::Str("br\""), Pat::Str("\"")),
566+
LitKind::ByteStrRaw(_) => (Pat::Str("br#\""), Pat::Str("#")),
567+
LitKind::CStr => (Pat::Str("c\""), Pat::Str("\"")),
568+
LitKind::CStrRaw(0) => (Pat::Str("cr\""), Pat::Str("\"")),
569+
LitKind::CStrRaw(_) => (Pat::Str("cr#\""), Pat::Str("#\"")),
570+
LitKind::Char => (Pat::Str("'"), Pat::Str("'")),
571+
LitKind::Float | LitKind::Integer => (Pat::Sym(lit.symbol), Pat::Sym(lit.suffix.unwrap_or(lit.symbol))),
572+
LitKind::Str => (Pat::Str("\""), Pat::Str("\"")),
573+
LitKind::StrRaw(0) => (Pat::Str("r"), Pat::Str("\"")),
574+
LitKind::StrRaw(_) => (Pat::Str("r#"), Pat::Str("#")),
575+
LitKind::Err(_) => (Pat::Str(""), Pat::Str("")),
576+
}
577+
}
578+
579+
/// Get the search patterns to use for the given expression
580+
#[expect(clippy::too_many_lines, reason = "just a big `match`")]
581+
fn ast_expr_search_pat(e: &ast::Expr) -> (Pat, Pat) {
582+
#[expect(clippy::too_many_lines, reason = "just a big `match`")]
583+
fn inner(e: &ast::Expr, outer_span: Span) -> (Pat, Pat) {
584+
use ast::{
585+
Block, BlockCheckMode, CaptureBy, Closure, ExprKind, GenBlockKind, MatchKind, MethodCall, UnsafeSource,
586+
YieldKind,
587+
};
588+
589+
// The expression can have subexpressions in different contexts, in which case
590+
// building up a search pattern from the macro expansion would lead to false positives;
591+
// e.g. `return format!(..)` would be considered to be from a proc macro
592+
// if we build up a pattern for the macro expansion and compare it to the invocation `format!()`.
593+
// So instead we return an empty pattern such that `span_matches_pat` always returns true.
594+
if !e.span.eq_ctxt(outer_span) {
595+
return (Pat::Str(""), Pat::Str(""));
596+
}
597+
598+
match &e.kind {
599+
ExprKind::Underscore => (Pat::Str("_"), Pat::Str("_")),
600+
ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")),
601+
ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), inner(e, outer_span).1),
602+
ExprKind::Unary(UnOp::Not, e) => (Pat::Str("!"), inner(e, outer_span).1),
603+
ExprKind::Unary(UnOp::Neg, e) => (Pat::Str("-"), inner(e, outer_span).1),
604+
ExprKind::Lit(lit) => token_lit_search_pat(lit),
605+
ExprKind::Paren(e) => inner(e, outer_span),
606+
ExprKind::Array(_) | ExprKind::Repeat(..) => (Pat::Str("["), Pat::Str("]")),
607+
ExprKind::Range(None, None, lims) => range_limits_search_pat(*lims),
608+
ExprKind::Range(None, Some(end), lims) => (range_limits_search_pat(*lims).0, inner(end, outer_span).1),
609+
ExprKind::Range(Some(start), None, lims) => (inner(start, outer_span).0, range_limits_search_pat(*lims).1),
610+
ExprKind::Call(e, args)
611+
| ExprKind::MethodCall(box MethodCall {
612+
seg: _,
613+
receiver: e,
614+
args,
615+
span: _,
616+
}) => (
617+
inner(e, outer_span).0,
618+
// Parenthesis are trimmed from the text before the search patterns are matched.
619+
// See: `span_matches_pat`
620+
match &**args {
621+
[] => Pat::Str("("),
622+
[.., last] => inner(last, outer_span).1,
623+
},
624+
),
625+
ExprKind::Binary(_, first, last)
626+
| ExprKind::Assign(first, last, _)
627+
| ExprKind::AssignOp(_, first, last)
628+
| ExprKind::Range(Some(first), Some(last), _) => {
629+
(inner(first, outer_span).0, inner(last, outer_span).1)
630+
},
631+
ExprKind::Tup(tup) => {
632+
match &**tup {
633+
// Parentheses are trimmed from the text before the search patterns are matched.
634+
// See: `span_matches_pat`
635+
[] => (Pat::Str(")"), Pat::Str("(")),
636+
[e] => inner(e, outer_span),
637+
[first, .., last] => (inner(first, outer_span).0, inner(last, outer_span).1),
638+
}
639+
},
640+
ExprKind::Cast(e, _) | ExprKind::Type(e, _) => (inner(e, outer_span).0, Pat::Str("")),
641+
ExprKind::Let(_, init, _, _) => (Pat::Str("let"), inner(init, outer_span).1),
642+
ExprKind::If(..) => (Pat::Str("if"), Pat::Str("}")),
643+
ExprKind::Loop(_, Some(_), _)
644+
| ExprKind::While(_, _, Some(_))
645+
| ExprKind::ForLoop { label: Some(_), .. }
646+
| ExprKind::Block(_, Some(_)) => (Pat::Str("'"), Pat::Str("}")),
647+
ExprKind::Loop(_, None, _) => (Pat::Str("loop"), Pat::Str("}")),
648+
ExprKind::While(_, _, None) => (Pat::Str("while"), Pat::Str("}")),
649+
ExprKind::ForLoop { label: None, .. } => (Pat::Str("for"), Pat::Str("}")),
650+
ExprKind::Match(_, _, MatchKind::Prefix) => (Pat::Str("match"), Pat::Str("}")),
651+
ExprKind::Match(e, _, MatchKind::Postfix) => (inner(e, outer_span).0, Pat::Str("}")),
652+
ExprKind::Try(e) => (inner(e, outer_span).0, Pat::Str("?")),
653+
ExprKind::TryBlock(_) => (Pat::Str("try"), Pat::Str("}")),
654+
ExprKind::Await(e, _) => (inner(e, outer_span).0, Pat::Str("await")),
655+
ExprKind::Closure(box Closure {
656+
capture_clause, body, ..
657+
}) => {
658+
let start = match capture_clause {
659+
CaptureBy::Value { .. } => "move",
660+
CaptureBy::Use { .. } => "use",
661+
CaptureBy::Ref => "|",
662+
};
663+
(Pat::Str(start), inner(body, outer_span).1)
664+
},
665+
ExprKind::Gen(_, _,GenBlockKind::Async | GenBlockKind::AsyncGen, _) => (Pat::Str("async"), Pat::Str("")),
666+
ExprKind::Gen(_, _,GenBlockKind::Gen, _) => (Pat::Str("gen"), Pat::Str("")),
667+
ExprKind::Block(
668+
box Block {
669+
rules: BlockCheckMode::Unsafe(UnsafeSource::UserProvided),
670+
..
671+
},
672+
None,
673+
) => (Pat::Str("unsafe"), Pat::Str("}")),
674+
ExprKind::Block(_, None) => (Pat::Str("{"), Pat::Str("}")),
675+
ExprKind::Field(e, name) => (inner(e, outer_span).0, Pat::Sym(name.name)),
676+
ExprKind::Index(e, _, _) => (inner(e, outer_span).0, Pat::Str("]")),
677+
ExprKind::Path(None, path) => ast_path_search_pat(path),
678+
ExprKind::Path(Some(_), path) => (Pat::Str("<"), ast_path_search_pat(path).1),
679+
ExprKind::AddrOf(_, _, e) => (Pat::Str("&"), inner(e, outer_span).1),
680+
ExprKind::Break(None, None) => (Pat::Str("break"), Pat::Str("break")),
681+
ExprKind::Break(Some(name), None) => (Pat::Str("break"), Pat::Sym(name.ident.name)),
682+
ExprKind::Break(_, Some(e)) => (Pat::Str("break"), inner(e, outer_span).1),
683+
ExprKind::Continue(None) => (Pat::Str("continue"), Pat::Str("continue")),
684+
ExprKind::Continue(Some(name)) => (Pat::Str("continue"), Pat::Sym(name.ident.name)),
685+
ExprKind::Ret(None) => (Pat::Str("return"), Pat::Str("return")),
686+
ExprKind::Ret(Some(e)) => (Pat::Str("return"), inner(e, outer_span).1),
687+
ExprKind::Become(e) => (Pat::Str("become"), inner(e, outer_span).1),
688+
ExprKind::Struct(box StructExpr { path, .. }) => (ast_path_search_pat(path).0, Pat::Str("")),
689+
ExprKind::Use(e, _) => (inner(e, outer_span).0, Pat::Str("use")),
690+
ExprKind::Yield(YieldKind::Prefix(_)) => (Pat::Str("yield"), Pat::Str("")),
691+
ExprKind::Yield(YieldKind::Postfix(_)) => (inner(e, outer_span).0, Pat::Str("")),
692+
ExprKind::OffsetOf(..)
693+
// Syntax unstable
694+
| ExprKind::Yeet(_) | ExprKind::UnsafeBinderCast(..)
695+
// Don't have a good `Pat` for `ByteSymbol`s
696+
| ExprKind::IncludedBytes(_)
697+
// We don't know how qualified the path to the macro was
698+
| ExprKind::FormatArgs(_) | ExprKind::InlineAsm(..)
699+
// Should've been expanded by now (?)
700+
| ExprKind::MacCall(..)
701+
// Dummy/placeholder
702+
| ExprKind::Err(_) | ExprKind::Dummy => (Pat::Str(""), Pat::Str("")),
703+
}
704+
}
705+
706+
inner(e, e.span)
707+
}
708+
709+
fn range_limits_search_pat(lims: RangeLimits) -> (Pat, Pat) {
710+
match lims {
711+
RangeLimits::HalfOpen => (Pat::Str(".."), Pat::Str("..")),
712+
RangeLimits::Closed => (Pat::Str("..="), Pat::Str("..=")),
713+
}
714+
}
715+
540716
fn ident_search_pat(ident: Ident) -> (Pat, Pat) {
541717
(Pat::Sym(ident.name), Pat::Sym(ident.name))
542718
}
@@ -572,6 +748,7 @@ impl_with_search_pat!((_cx: LateContext<'tcx>, self: Path<'_>) => path_search_pa
572748

573749
impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: Attribute) => attr_search_pat(self));
574750
impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: ast::Ty) => ast_ty_search_pat(self));
751+
impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: ast::Expr) => ast_expr_search_pat(self));
575752

576753
impl<'cx> WithSearchPat<'cx> for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
577754
type Context = LateContext<'cx>;

0 commit comments

Comments
 (0)