Skip to content

Commit 2b98b7b

Browse files
committed
feat(utils/check_proc_macro): impl WithSearchPat for ast::Expr
1 parent 8f99ddd commit 2b98b7b

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;
@@ -418,6 +419,22 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
418419
}
419420
}
420421

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

@@ -536,6 +553,167 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) {
536553
}
537554
}
538555

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

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

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

0 commit comments

Comments
 (0)