1use rustc_abi::ExternAbi;
16use rustc_ast as ast;
17use rustc_ast::AttrStyle;
18use rustc_ast::ast::{
19 AttrKind, Attribute, GenericArgs, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy,
20};
21use rustc_ast::token::CommentKind;
22use rustc_hir::intravisit::FnKind;
23use rustc_hir::{
24 Block, BlockCheckMode, Body, BoundConstness, BoundPolarity, Closure, Destination, Expr, ExprKind, FieldDef,
25 FnHeader, FnRetTy, HirId, Impl, ImplItem, ImplItemImplKind, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource,
26 MatchSource, MutTy, Node, Path, PolyTraitRef, QPath, Safety, TraitBoundModifiers, TraitImplHeader, TraitItem,
27 TraitItemKind, TraitRef, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource,
28};
29use rustc_lint::{EarlyContext, LateContext, LintContext};
30use rustc_middle::ty::TyCtxt;
31use rustc_session::Session;
32use rustc_span::symbol::{Ident, kw};
33use rustc_span::{Span, Symbol, sym};
34
35#[derive(Clone)]
37pub enum Pat {
38 Str(&'static str),
40 MultiStr(&'static [&'static str]),
42 OwnedMultiStr(Vec<String>),
44 Sym(Symbol),
46 Num,
48 Attr(Symbol),
50}
51
52fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) -> bool {
55 let pos = sess.source_map().lookup_byte_offset(span.lo());
56 let Some(ref src) = pos.sf.src else {
57 return false;
58 };
59 let end = span.hi() - pos.sf.start_pos;
60 src.get(pos.pos.0 as usize..end.0 as usize).is_some_and(|s| {
61 let start_str = s.trim_start_matches(|c: char| c.is_whitespace() || c == '(');
63 let end_str = s.trim_end_matches(|c: char| c.is_whitespace() || c == ')' || c == ',');
64 (match start_pat {
65 Pat::Str(text) => start_str.starts_with(text),
66 Pat::MultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
67 Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
68 Pat::Sym(sym) => start_str.starts_with(sym.as_str()),
69 Pat::Num => start_str.as_bytes().first().is_some_and(u8::is_ascii_digit),
70 Pat::Attr(sym) => {
71 let start_str = start_str
72 .strip_prefix("#[")
73 .or_else(|| start_str.strip_prefix("#!["))
74 .unwrap_or(start_str);
75 start_str.trim_start().starts_with(sym.as_str())
76 },
77 } && match end_pat {
78 Pat::Str(text) => end_str.ends_with(text),
79 Pat::MultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
80 Pat::OwnedMultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
81 Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
82 Pat::Num => end_str.as_bytes().last().is_some_and(u8::is_ascii_hexdigit),
83 Pat::Attr(_) => false,
84 })
85 })
86}
87
88fn lit_search_pat(lit: &LitKind) -> (Pat, Pat) {
90 match lit {
91 LitKind::Str(_, StrStyle::Cooked) => (Pat::Str("\""), Pat::Str("\"")),
92 LitKind::Str(_, StrStyle::Raw(0)) => (Pat::Str("r"), Pat::Str("\"")),
93 LitKind::Str(_, StrStyle::Raw(_)) => (Pat::Str("r#"), Pat::Str("#")),
94 LitKind::ByteStr(_, StrStyle::Cooked) => (Pat::Str("b\""), Pat::Str("\"")),
95 LitKind::ByteStr(_, StrStyle::Raw(0)) => (Pat::Str("br\""), Pat::Str("\"")),
96 LitKind::ByteStr(_, StrStyle::Raw(_)) => (Pat::Str("br#\""), Pat::Str("#")),
97 LitKind::Byte(_) => (Pat::Str("b'"), Pat::Str("'")),
98 LitKind::Char(_) => (Pat::Str("'"), Pat::Str("'")),
99 LitKind::Int(_, LitIntType::Signed(IntTy::Isize)) => (Pat::Num, Pat::Str("isize")),
100 LitKind::Int(_, LitIntType::Unsigned(UintTy::Usize)) => (Pat::Num, Pat::Str("usize")),
101 LitKind::Int(..) => (Pat::Num, Pat::Num),
102 LitKind::Float(..) => (Pat::Num, Pat::Str("")),
103 LitKind::Bool(true) => (Pat::Str("true"), Pat::Str("true")),
104 LitKind::Bool(false) => (Pat::Str("false"), Pat::Str("false")),
105 _ => (Pat::Str(""), Pat::Str("")),
106 }
107}
108
109fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) {
111 match path {
112 QPath::Resolved(ty, path) => {
113 let start = if ty.is_some() {
114 Pat::Str("<")
115 } else {
116 path.segments.first().map_or(Pat::Str(""), |seg| {
117 if seg.ident.name == kw::PathRoot {
118 Pat::Str("::")
119 } else {
120 Pat::Sym(seg.ident.name)
121 }
122 })
123 };
124 let end = path.segments.last().map_or(Pat::Str(""), |seg| {
125 if seg.args.is_some() {
126 Pat::Str(">")
127 } else {
128 Pat::Sym(seg.ident.name)
129 }
130 });
131 (start, end)
132 },
133 QPath::TypeRelative(_, name) => (Pat::Str(""), Pat::Sym(name.ident.name)),
134 }
135}
136
137fn path_search_pat(path: &Path<'_>) -> (Pat, Pat) {
138 let (head, tail) = match path.segments {
139 [] => return (Pat::Str(""), Pat::Str("")),
140 [p] => (Pat::Sym(p.ident.name), p),
141 [.., tail] => (Pat::Str(""), tail),
144 };
145 (
146 head,
147 if tail.args.is_some() {
148 Pat::Str(">")
149 } else {
150 Pat::Sym(tail.ident.name)
151 },
152 )
153}
154
155fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
157 fn expr_search_pat_inner(tcx: TyCtxt<'_>, e: &Expr<'_>, outer_span: Span) -> (Pat, Pat) {
158 if !e.span.eq_ctxt(outer_span) {
164 return (Pat::Str(""), Pat::Str(""));
165 }
166
167 match e.kind {
168 ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")),
169 ExprKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
172 ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat_inner(tcx, e, outer_span).1),
173 ExprKind::Unary(UnOp::Not, e) => (Pat::Str("!"), expr_search_pat_inner(tcx, e, outer_span).1),
174 ExprKind::Unary(UnOp::Neg, e) => (Pat::Str("-"), expr_search_pat_inner(tcx, e, outer_span).1),
175 ExprKind::Lit(lit) => lit_search_pat(&lit.node),
176 ExprKind::Array(_) | ExprKind::Repeat(..) => (Pat::Str("["), Pat::Str("]")),
177 ExprKind::Call(e, []) | ExprKind::MethodCall(_, e, [], _) => {
178 (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("("))
179 },
180 ExprKind::Call(first, [.., last])
181 | ExprKind::MethodCall(_, first, [.., last], _)
182 | ExprKind::Binary(_, first, last)
183 | ExprKind::Tup([first, .., last])
184 | ExprKind::Assign(first, last, _)
185 | ExprKind::AssignOp(_, first, last) => (
186 expr_search_pat_inner(tcx, first, outer_span).0,
187 expr_search_pat_inner(tcx, last, outer_span).1,
188 ),
189 ExprKind::Tup([e]) | ExprKind::DropTemps(e) => expr_search_pat_inner(tcx, e, outer_span),
190 ExprKind::Cast(e, _) | ExprKind::Type(e, _) => (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("")),
191 ExprKind::Let(let_expr) => (Pat::Str("let"), expr_search_pat_inner(tcx, let_expr.init, outer_span).1),
192 ExprKind::If(..) => (Pat::Str("if"), Pat::Str("}")),
193 ExprKind::Loop(_, Some(_), _, _) | ExprKind::Block(_, Some(_)) => (Pat::Str("'"), Pat::Str("}")),
194 ExprKind::Loop(_, None, LoopSource::Loop, _) => (Pat::Str("loop"), Pat::Str("}")),
195 ExprKind::Loop(_, None, LoopSource::While, _) => (Pat::Str("while"), Pat::Str("}")),
196 ExprKind::Loop(_, None, LoopSource::ForLoop, _) | ExprKind::Match(_, _, MatchSource::ForLoopDesugar) => {
197 (Pat::Str("for"), Pat::Str("}"))
198 },
199 ExprKind::Match(_, _, MatchSource::Normal) => (Pat::Str("match"), Pat::Str("}")),
200 ExprKind::Match(e, _, MatchSource::TryDesugar(_)) => {
201 (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("?"))
202 },
203 ExprKind::Match(e, _, MatchSource::AwaitDesugar) | ExprKind::Yield(e, YieldSource::Await { .. }) => {
204 (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("await"))
205 },
206 ExprKind::Closure(&Closure { body, .. }) => (
207 Pat::Str(""),
208 expr_search_pat_inner(tcx, tcx.hir_body(body).value, outer_span).1,
209 ),
210 ExprKind::Block(
211 Block {
212 rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
213 ..
214 },
215 None,
216 ) => (Pat::Str("unsafe"), Pat::Str("}")),
217 ExprKind::Block(_, None) => (Pat::Str("{"), Pat::Str("}")),
218 ExprKind::Field(e, name) => (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Sym(name.name)),
219 ExprKind::Index(e, _, _) => (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("]")),
220 ExprKind::Path(ref path) => qpath_search_pat(path),
221 ExprKind::AddrOf(_, _, e) => (Pat::Str("&"), expr_search_pat_inner(tcx, e, outer_span).1),
222 ExprKind::Break(Destination { label: None, .. }, None) => (Pat::Str("break"), Pat::Str("break")),
223 ExprKind::Break(Destination { label: Some(name), .. }, None) => {
224 (Pat::Str("break"), Pat::Sym(name.ident.name))
225 },
226 ExprKind::Break(_, Some(e)) => (Pat::Str("break"), expr_search_pat_inner(tcx, e, outer_span).1),
227 ExprKind::Continue(Destination { label: None, .. }) => (Pat::Str("continue"), Pat::Str("continue")),
228 ExprKind::Continue(Destination { label: Some(name), .. }) => {
229 (Pat::Str("continue"), Pat::Sym(name.ident.name))
230 },
231 ExprKind::Ret(None) => (Pat::Str("return"), Pat::Str("return")),
232 ExprKind::Ret(Some(e)) => (Pat::Str("return"), expr_search_pat_inner(tcx, e, outer_span).1),
233 ExprKind::Struct(path, _, _) => (qpath_search_pat(path).0, Pat::Str("}")),
234 ExprKind::Yield(e, YieldSource::Yield) => (Pat::Str("yield"), expr_search_pat_inner(tcx, e, outer_span).1),
235 _ => (Pat::Str(""), Pat::Str("")),
236 }
237 }
238
239 expr_search_pat_inner(tcx, e, e.span)
240}
241
242fn fn_header_search_pat(header: FnHeader) -> Pat {
243 if header.is_async() {
244 Pat::Str("async")
245 } else if matches!(header.constness, rustc_hir::Constness::Const { always: false }) {
246 Pat::Str("const")
247 } else if header.is_unsafe() {
248 Pat::Str("unsafe")
249 } else if header.abi != ExternAbi::Rust {
250 Pat::Str("extern")
251 } else {
252 Pat::MultiStr(&["fn", "extern"])
253 }
254}
255
256fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) {
257 let (start_pat, end_pat) = match &item.kind {
258 ItemKind::ExternCrate(..) => (Pat::Str("extern"), Pat::Str(";")),
259 ItemKind::Static(..) => (Pat::Str("static"), Pat::Str(";")),
260 ItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
261 ItemKind::Fn { sig, .. } => (fn_header_search_pat(sig.header), Pat::Str("")),
262 ItemKind::ForeignMod { .. } => (Pat::Str("extern"), Pat::Str("}")),
263 ItemKind::TyAlias(..) => (Pat::Str("type"), Pat::Str(";")),
264 ItemKind::Enum(..) => (Pat::Str("enum"), Pat::Str("}")),
265 ItemKind::Struct(_, _, VariantData::Struct { .. }) => (Pat::Str("struct"), Pat::Str("}")),
266 ItemKind::Struct(..) => (Pat::Str("struct"), Pat::Str(";")),
267 ItemKind::Union(..) => (Pat::Str("union"), Pat::Str("}")),
268 ItemKind::Trait {
269 safety: Safety::Unsafe, ..
270 }
271 | ItemKind::Impl(Impl {
272 of_trait: Some(TraitImplHeader {
273 safety: Safety::Unsafe, ..
274 }),
275 ..
276 }) => (Pat::Str("unsafe"), Pat::Str("}")),
277 ItemKind::Trait {
278 is_auto: IsAuto::Yes, ..
279 } => (Pat::Str("auto"), Pat::Str("}")),
280 ItemKind::Trait { .. } => (Pat::Str("trait"), Pat::Str("}")),
281 ItemKind::Impl(_) => (Pat::Str("impl"), Pat::Str("}")),
282 ItemKind::Mod(..) => (Pat::Str("mod"), Pat::Str("")),
283 ItemKind::Macro(_, def, _) => (
284 Pat::Str(if def.macro_rules { "macro_rules" } else { "macro" }),
285 Pat::Str(""),
286 ),
287 ItemKind::TraitAlias(..) => (Pat::Str("trait"), Pat::Str(";")),
288 ItemKind::GlobalAsm { .. } => return (Pat::Str("global_asm"), Pat::Str("")),
289 ItemKind::Use(..) => return (Pat::Str(""), Pat::Str("")),
290 };
291 if item.vis_span.is_empty() {
292 (start_pat, end_pat)
293 } else {
294 (Pat::Str("pub"), end_pat)
295 }
296}
297
298fn trait_item_search_pat(item: &TraitItem<'_>) -> (Pat, Pat) {
299 match &item.kind {
300 TraitItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
301 TraitItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")),
302 TraitItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
303 }
304}
305
306fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) {
307 let (mut start_pat, end_pat) = match &item.kind {
308 ImplItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
309 ImplItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")),
310 ImplItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
311 };
312 if let ImplItemImplKind::Inherent { vis_span, .. } = item.impl_kind
313 && !vis_span.is_empty()
314 {
315 start_pat = Pat::Str("pub");
316 }
317 (start_pat, end_pat)
318}
319
320fn field_def_search_pat(def: &FieldDef<'_>) -> (Pat, Pat) {
321 if def.vis_span.is_empty() {
322 if def.is_positional() {
323 (Pat::Str(""), Pat::Str(""))
324 } else {
325 (Pat::Sym(def.ident.name), Pat::Str(""))
326 }
327 } else {
328 (Pat::Str("pub"), Pat::Str(""))
329 }
330}
331
332fn variant_search_pat(v: &Variant<'_>) -> (Pat, Pat) {
333 match v.data {
334 VariantData::Struct { .. } => (Pat::Sym(v.ident.name), Pat::Str("}")),
335 VariantData::Tuple(..) => (Pat::Sym(v.ident.name), Pat::Str("")),
336 VariantData::Unit(..) => (Pat::Sym(v.ident.name), Pat::Sym(v.ident.name)),
337 }
338}
339
340fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirId) -> (Pat, Pat) {
341 let (mut start_pat, end_pat) = match kind {
342 FnKind::ItemFn(.., header) => (fn_header_search_pat(*header), Pat::Str("")),
343 FnKind::Method(.., sig) => (fn_header_search_pat(sig.header), Pat::Str("")),
344 FnKind::Closure => return (Pat::Str(""), expr_search_pat(tcx, body.value).1),
345 };
346 match tcx.hir_node(hir_id) {
347 Node::Item(Item { vis_span, .. })
348 | Node::ImplItem(ImplItem {
349 impl_kind: ImplItemImplKind::Inherent { vis_span, .. },
350 ..
351 }) => {
352 if !vis_span.is_empty() {
353 start_pat = Pat::Str("pub");
354 }
355 },
356 Node::ImplItem(_) | Node::TraitItem(_) => {},
357 _ => start_pat = Pat::Str(""),
358 }
359 (start_pat, end_pat)
360}
361
362fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
363 match attr.kind {
364 AttrKind::Normal(..) => {
365 if let Some(name) = attr.name() {
366 (Pat::Attr(name), Pat::Str(""))
368 } else {
369 (Pat::Str("#"), Pat::Str("]"))
370 }
371 },
372 AttrKind::Synthetic(..) => unreachable!(),
373 AttrKind::DocComment(_kind @ CommentKind::Line, ..) => {
374 if attr.style == AttrStyle::Outer {
375 (Pat::Str("///"), Pat::Str(""))
376 } else {
377 (Pat::Str("//!"), Pat::Str(""))
378 }
379 },
380 AttrKind::DocComment(_kind @ CommentKind::Block, ..) => {
381 if attr.style == AttrStyle::Outer {
382 (Pat::Str("/**"), Pat::Str("*/"))
383 } else {
384 (Pat::Str("/*!"), Pat::Str("*/"))
385 }
386 },
387 }
388}
389
390fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
391 match ty.kind {
392 TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")),
393 TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ty_search_pat(ty).1),
394 TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ty_search_pat(ty).1),
395 TyKind::FnPtr(fn_ptr) => (
396 if fn_ptr.safety.is_unsafe() {
397 Pat::Str("unsafe")
398 } else if fn_ptr.abi != ExternAbi::Rust {
399 Pat::Str("extern")
400 } else {
401 Pat::MultiStr(&["fn", "extern"])
402 },
403 match fn_ptr.decl.output {
404 FnRetTy::DefaultReturn(_) => {
405 if let [.., ty] = fn_ptr.decl.inputs {
406 ty_search_pat(ty).1
407 } else {
408 Pat::Str("(")
409 }
410 },
411 FnRetTy::Return(ty) => ty_search_pat(ty).1,
412 },
413 ),
414 TyKind::Never => (Pat::Str("!"), Pat::Str("!")),
415 TyKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
418 TyKind::Tup([ty]) => ty_search_pat(ty),
419 TyKind::Tup([head, .., tail]) => (ty_search_pat(head).0, ty_search_pat(tail).1),
420 TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")),
421 TyKind::Path(qpath) => qpath_search_pat(&qpath),
422 TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")),
423 TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ty_search_pat(binder_ty.inner_ty).1),
424 TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => {
425 (Pat::Str("dyn"), Pat::Str(""))
426 },
427 _ => (Pat::Str(""), Pat::Str("")),
429 }
430}
431
432fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) {
433 use ast::{Extern, FnRetTy, MutTy, Safety, TraitObjectSyntax, TyKind};
434
435 match &ty.kind {
436 TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")),
437 TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ast_ty_search_pat(ty).1),
438 TyKind::Ref(_, MutTy { ty, .. }) | TyKind::PinnedRef(_, MutTy { ty, .. }) => {
439 (Pat::Str("&"), ast_ty_search_pat(ty).1)
440 },
441 TyKind::FnPtr(fn_ptr) => (
442 if let Safety::Unsafe(_) = fn_ptr.safety {
443 Pat::Str("unsafe")
444 } else if let Extern::Explicit(strlit, _) = fn_ptr.ext
445 && strlit.symbol == sym::rust
446 {
447 Pat::MultiStr(&["fn", "extern"])
448 } else {
449 Pat::Str("extern")
450 },
451 match &fn_ptr.decl.output {
452 FnRetTy::Default(_) => {
453 if let [.., param] = &*fn_ptr.decl.inputs {
454 ast_ty_search_pat(¶m.ty).1
455 } else {
456 Pat::Str("(")
457 }
458 },
459 FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1,
460 },
461 ),
462 TyKind::Never => (Pat::Str("!"), Pat::Str("!")),
463 TyKind::Tup(tup) => match &**tup {
466 [] => (Pat::Str(")"), Pat::Str("(")),
467 [ty] => ast_ty_search_pat(ty),
468 [head, .., tail] => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1),
469 },
470 TyKind::ImplTrait(..) => (Pat::Str("impl"), Pat::Str("")),
471 TyKind::Path(qself_path, path) => {
472 let start = if qself_path.is_some() {
473 Pat::Str("<")
474 } else if let Some(first) = path.segments.first() {
475 ident_search_pat(first.ident).0
476 } else {
477 Pat::Str("")
479 };
480 let end = if let Some(last) = path.segments.last() {
481 match last.args.as_deref() {
482 Some(GenericArgs::AngleBracketed(_)) => Pat::Str(">"),
484 Some(GenericArgs::Parenthesized(par_args)) => match &par_args.output {
485 FnRetTy::Default(_) => {
486 if let Some(last) = par_args.inputs.last() {
487 ast_ty_search_pat(last).1
489 } else {
490 Pat::Str("(")
492 }
493 },
494 FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1,
496 },
497 Some(GenericArgs::ParenthesizedElided(_)) => Pat::Str(".."),
499 None => ident_search_pat(last.ident).1,
501 }
502 } else {
503 Pat::Str(
505 if qself_path.is_some() {
506 ">" } else {
508 ""
509 }
510 )
511 };
512 (start, end)
513 },
514 TyKind::Infer => (Pat::Str("_"), Pat::Str("_")),
515 TyKind::Paren(ty) => ast_ty_search_pat(ty),
516 TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ast_ty_search_pat(&binder_ty.inner_ty).1),
517 TyKind::TraitObject(_, trait_obj_syntax) => {
518 if let TraitObjectSyntax::Dyn = trait_obj_syntax {
519 (Pat::Str("dyn"), Pat::Str(""))
520 } else {
521 (Pat::Str(""), Pat::Str(""))
523 }
524 },
525 TyKind::MacCall(mac_call) => {
526 let start = if let Some(first) = mac_call.path.segments.first() {
527 ident_search_pat(first.ident).0
528 } else {
529 Pat::Str("")
530 };
531 (start, Pat::Str(""))
532 },
533
534 TyKind::ImplicitSelf
536
537 | TyKind::Pat(..)
539 | TyKind::FieldOf(..)
540 | TyKind::View(..)
541 | TyKind::DirectConstArg(..)
542
543 | TyKind::CVarArgs
545
546 | TyKind::Dummy
548 | TyKind::Err(_) => (Pat::Str(""), Pat::Str("")),
549 }
550}
551
552fn trait_ref_search_pat(trait_ref: &TraitRef<'_>) -> (Pat, Pat) {
555 path_search_pat(trait_ref.path)
556}
557
558fn poly_trait_ref_search_pat(poly_trait_ref: &PolyTraitRef<'_>) -> (Pat, Pat) {
559 let PolyTraitRef {
563 modifiers: TraitBoundModifiers { constness, polarity },
564 trait_ref,
565 ..
566 } = poly_trait_ref;
567
568 let trait_ref_search_pat = trait_ref_search_pat(trait_ref);
569
570 #[expect(
571 clippy::unnecessary_lazy_evaluations,
572 reason = "the closure in `or_else` has `match polarity`, which isn't free"
573 )]
574 let start = match constness {
575 BoundConstness::Never => None,
576 BoundConstness::Maybe(_) => Some(Pat::Str("[const]")),
577 BoundConstness::Always(_) => Some(Pat::Str("const")),
578 }
579 .or_else(|| match polarity {
580 BoundPolarity::Negative(_) => Some(Pat::Str("!")),
581 BoundPolarity::Maybe(_) => Some(Pat::Str("?")),
582 BoundPolarity::Positive => None,
583 })
584 .unwrap_or(trait_ref_search_pat.0);
585 let end = trait_ref_search_pat.1;
586
587 (start, end)
588}
589
590fn ident_search_pat(ident: Ident) -> (Pat, Pat) {
591 (Pat::Sym(ident.name), Pat::Sym(ident.name))
592}
593
594pub trait WithSearchPat<'cx> {
595 type Context: LintContext;
596 fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat);
597 fn span(&self) -> Span;
598}
599macro_rules! impl_with_search_pat {
600 (($cx_ident:ident: $cx_ty:ident<$cx_lt:lifetime>, $self:tt: $ty:ty) => $fn:ident($($args:tt)*)) => {
601 impl<$cx_lt> WithSearchPat<$cx_lt> for $ty {
602 type Context = $cx_ty<$cx_lt>;
603 fn search_pat(&$self, $cx_ident: &Self::Context) -> (Pat, Pat) {
604 $fn($($args)*)
605 }
606 fn span(&self) -> Span {
607 self.span
608 }
609 }
610 };
611}
612impl_with_search_pat!((cx: LateContext<'tcx>, self: Expr<'tcx>) => expr_search_pat(cx.tcx, self));
613impl_with_search_pat!((_cx: LateContext<'tcx>, self: Item<'_>) => item_search_pat(self));
614impl_with_search_pat!((_cx: LateContext<'tcx>, self: TraitItem<'_>) => trait_item_search_pat(self));
615impl_with_search_pat!((_cx: LateContext<'tcx>, self: ImplItem<'_>) => impl_item_search_pat(self));
616impl_with_search_pat!((_cx: LateContext<'tcx>, self: FieldDef<'_>) => field_def_search_pat(self));
617impl_with_search_pat!((_cx: LateContext<'tcx>, self: Variant<'_>) => variant_search_pat(self));
618impl_with_search_pat!((_cx: LateContext<'tcx>, self: Ty<'_>) => ty_search_pat(self));
619impl_with_search_pat!((_cx: LateContext<'tcx>, self: Ident) => ident_search_pat(*self));
620impl_with_search_pat!((_cx: LateContext<'tcx>, self: Lit) => lit_search_pat(&self.node));
621impl_with_search_pat!((_cx: LateContext<'tcx>, self: Path<'_>) => path_search_pat(self));
622impl_with_search_pat!((_cx: LateContext<'tcx>, self: PolyTraitRef<'_>) => poly_trait_ref_search_pat(self));
623
624impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: Attribute) => attr_search_pat(self));
625impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: ast::Ty) => ast_ty_search_pat(self));
626
627impl<'cx> WithSearchPat<'cx> for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
628 type Context = LateContext<'cx>;
629
630 fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat) {
631 fn_kind_pat(cx.tcx, self.0, self.1, self.2)
632 }
633
634 fn span(&self) -> Span {
635 self.3
636 }
637}
638
639pub fn is_from_proc_macro<'cx, T: WithSearchPat<'cx>>(cx: &T::Context, item: &T) -> bool {
644 let (start_pat, end_pat) = item.search_pat(cx);
645 !span_matches_pat(cx.sess(), item.span(), start_pat, end_pat)
646}
647
648pub fn is_span_match(cx: &impl LintContext, span: Span) -> bool {
650 span_matches_pat(cx.sess(), span, Pat::Str("match"), Pat::Str("}"))
651}
652
653pub fn is_span_if(cx: &impl LintContext, span: Span) -> bool {
655 span_matches_pat(cx.sess(), span, Pat::Str("if"), Pat::Str("}"))
656}