Skip to content

Commit 395cfe0

Browse files
committed
feat(utils/check_proc_macro): impl WithSearchPat for ast::Expr
1 parent 6b80a9d commit 395cfe0

File tree

1 file changed

+180
-1
lines changed

1 file changed

+180
-1
lines changed

clippy_utils/src/check_proc_macro.rs

Lines changed: 180 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,167 @@ 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+
// Parentheses are trimmed from the text before the search patterns are matched.
606+
// See: `span_matches_pat`
607+
ExprKind::Paren(e) => inner(e, outer_span),
608+
ExprKind::Array(_) | ExprKind::Repeat(..) => (Pat::Str("["), Pat::Str("]")),
609+
ExprKind::Range(None, None, lims) => range_limits_search_pat(*lims),
610+
ExprKind::Range(None, Some(end), lims) => (range_limits_search_pat(*lims).0, inner(end, outer_span).1),
611+
ExprKind::Range(Some(start), None, lims) => (inner(start, outer_span).0, range_limits_search_pat(*lims).1),
612+
ExprKind::Call(e, args)
613+
| ExprKind::MethodCall(box MethodCall {
614+
seg: _,
615+
receiver: e,
616+
args,
617+
span: _,
618+
}) => (
619+
inner(e, outer_span).0,
620+
// Parenthesis are trimmed from the text before the search patterns are matched.
621+
// See: `span_matches_pat`
622+
match &**args {
623+
[] => Pat::Str("("),
624+
[.., last] => inner(last, outer_span).1,
625+
},
626+
),
627+
ExprKind::Binary(_, first, last)
628+
| ExprKind::Assign(first, last, _)
629+
| ExprKind::AssignOp(_, first, last)
630+
| ExprKind::Range(Some(first), Some(last), _) => {
631+
(inner(first, outer_span).0, inner(last, outer_span).1)
632+
},
633+
ExprKind::Tup(tup) => {
634+
match &**tup {
635+
// Parentheses are trimmed from the text before the search patterns are matched.
636+
// See: `span_matches_pat`
637+
[] => (Pat::Str(")"), Pat::Str("(")),
638+
[e] => inner(e, outer_span),
639+
[first, .., last] => (inner(first, outer_span).0, inner(last, outer_span).1),
640+
}
641+
},
642+
ExprKind::Cast(e, _) | ExprKind::Type(e, _) => (inner(e, outer_span).0, Pat::Str("")),
643+
ExprKind::Let(_, init, _, _) => (Pat::Str("let"), inner(init, outer_span).1),
644+
ExprKind::If(..) => (Pat::Str("if"), Pat::Str("}")),
645+
ExprKind::Loop(_, Some(_), _)
646+
| ExprKind::While(_, _, Some(_))
647+
| ExprKind::ForLoop { label: Some(_), .. }
648+
| ExprKind::Block(_, Some(_)) => (Pat::Str("'"), Pat::Str("}")),
649+
ExprKind::Loop(_, None, _) => (Pat::Str("loop"), Pat::Str("}")),
650+
ExprKind::While(_, _, None) => (Pat::Str("while"), Pat::Str("}")),
651+
ExprKind::ForLoop { label: None, .. } => (Pat::Str("for"), Pat::Str("}")),
652+
ExprKind::Match(_, _, MatchKind::Prefix) => (Pat::Str("match"), Pat::Str("}")),
653+
ExprKind::Match(e, _, MatchKind::Postfix) => (inner(e, outer_span).0, Pat::Str("}")),
654+
ExprKind::Try(e) => (inner(e, outer_span).0, Pat::Str("?")),
655+
ExprKind::TryBlock(_) => (Pat::Str("try"), Pat::Str("}")),
656+
ExprKind::Await(e, _) => (inner(e, outer_span).0, Pat::Str("await")),
657+
ExprKind::Closure(box Closure {
658+
capture_clause, body, ..
659+
}) => {
660+
let start = match capture_clause {
661+
CaptureBy::Value { .. } => "move",
662+
CaptureBy::Use { .. } => "use",
663+
CaptureBy::Ref => "|",
664+
};
665+
(Pat::Str(start), inner(body, outer_span).1)
666+
},
667+
ExprKind::Gen(_, _,GenBlockKind::Async | GenBlockKind::AsyncGen, _) => (Pat::Str("async"), Pat::Str("")),
668+
ExprKind::Gen(_, _,GenBlockKind::Gen, _) => (Pat::Str("gen"), Pat::Str("")),
669+
ExprKind::Block(
670+
box Block {
671+
rules: BlockCheckMode::Unsafe(UnsafeSource::UserProvided),
672+
..
673+
},
674+
None,
675+
) => (Pat::Str("unsafe"), Pat::Str("}")),
676+
ExprKind::Block(_, None) => (Pat::Str("{"), Pat::Str("}")),
677+
ExprKind::Field(e, name) => (inner(e, outer_span).0, Pat::Sym(name.name)),
678+
ExprKind::Index(e, _, _) => (inner(e, outer_span).0, Pat::Str("]")),
679+
ExprKind::Path(None, path) => ast_path_search_pat(path),
680+
ExprKind::Path(Some(_), path) => (Pat::Str("<"), ast_path_search_pat(path).1),
681+
ExprKind::AddrOf(_, _, e) => (Pat::Str("&"), inner(e, outer_span).1),
682+
ExprKind::Break(None, None) => (Pat::Str("break"), Pat::Str("break")),
683+
ExprKind::Break(Some(name), None) => (Pat::Str("break"), Pat::Sym(name.ident.name)),
684+
ExprKind::Break(_, Some(e)) => (Pat::Str("break"), inner(e, outer_span).1),
685+
ExprKind::Continue(None) => (Pat::Str("continue"), Pat::Str("continue")),
686+
ExprKind::Continue(Some(name)) => (Pat::Str("continue"), Pat::Sym(name.ident.name)),
687+
ExprKind::Ret(None) => (Pat::Str("return"), Pat::Str("return")),
688+
ExprKind::Ret(Some(e)) => (Pat::Str("return"), inner(e, outer_span).1),
689+
ExprKind::Become(e) => (Pat::Str("become"), inner(e, outer_span).1),
690+
ExprKind::Struct(box StructExpr { path, .. }) => (ast_path_search_pat(path).0, Pat::Str("")),
691+
ExprKind::Use(e, _) => (inner(e, outer_span).0, Pat::Str("use")),
692+
ExprKind::Yield(YieldKind::Prefix(_)) => (Pat::Str("yield"), Pat::Str("")),
693+
ExprKind::Yield(YieldKind::Postfix(_)) => (inner(e, outer_span).0, Pat::Str("")),
694+
ExprKind::OffsetOf(..)
695+
// Syntax unstable
696+
| ExprKind::Yeet(_) | ExprKind::UnsafeBinderCast(..)
697+
// Don't have a good `Pat` for `ByteSymbol`s
698+
| ExprKind::IncludedBytes(_)
699+
// We don't know how qualified the path to the macro was
700+
| ExprKind::FormatArgs(_) | ExprKind::InlineAsm(..)
701+
// Should've been expanded by now (?)
702+
| ExprKind::MacCall(..)
703+
// Dummy/placeholder
704+
| ExprKind::Err(_) | ExprKind::Dummy => (Pat::Str(""), Pat::Str("")),
705+
}
706+
}
707+
708+
inner(e, e.span)
709+
}
710+
711+
fn range_limits_search_pat(lims: RangeLimits) -> (Pat, Pat) {
712+
match lims {
713+
RangeLimits::HalfOpen => (Pat::Str(".."), Pat::Str("..")),
714+
RangeLimits::Closed => (Pat::Str("..="), Pat::Str("..=")),
715+
}
716+
}
717+
540718
fn ident_search_pat(ident: Ident) -> (Pat, Pat) {
541719
(Pat::Sym(ident.name), Pat::Sym(ident.name))
542720
}
@@ -572,6 +750,7 @@ impl_with_search_pat!((_cx: LateContext<'tcx>, self: Path<'_>) => path_search_pa
572750

573751
impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: Attribute) => attr_search_pat(self));
574752
impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: ast::Ty) => ast_ty_search_pat(self));
753+
impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: ast::Expr) => ast_expr_search_pat(self));
575754

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

0 commit comments

Comments
 (0)