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, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl,
25 ImplItem, ImplItemImplKind, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path,
26 QPath, Safety, TraitImplHeader, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData,
27 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}
49
50fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) -> bool {
53 let pos = sess.source_map().lookup_byte_offset(span.lo());
54 let Some(ref src) = pos.sf.src else {
55 return false;
56 };
57 let end = span.hi() - pos.sf.start_pos;
58 src.get(pos.pos.0 as usize..end.0 as usize).is_some_and(|s| {
59 let start_str = s.trim_start_matches(|c: char| c.is_whitespace() || c == '(');
61 let end_str = s.trim_end_matches(|c: char| c.is_whitespace() || c == ')' || c == ',');
62 (match start_pat {
63 Pat::Str(text) => start_str.starts_with(text),
64 Pat::MultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
65 Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
66 Pat::Sym(sym) => start_str.starts_with(sym.as_str()),
67 Pat::Num => start_str.as_bytes().first().is_some_and(u8::is_ascii_digit),
68 } && match end_pat {
69 Pat::Str(text) => end_str.ends_with(text),
70 Pat::MultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
71 Pat::OwnedMultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
72 Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
73 Pat::Num => end_str.as_bytes().last().is_some_and(u8::is_ascii_hexdigit),
74 })
75 })
76}
77
78fn lit_search_pat(lit: &LitKind) -> (Pat, Pat) {
80 match lit {
81 LitKind::Str(_, StrStyle::Cooked) => (Pat::Str("\""), Pat::Str("\"")),
82 LitKind::Str(_, StrStyle::Raw(0)) => (Pat::Str("r"), Pat::Str("\"")),
83 LitKind::Str(_, StrStyle::Raw(_)) => (Pat::Str("r#"), Pat::Str("#")),
84 LitKind::ByteStr(_, StrStyle::Cooked) => (Pat::Str("b\""), Pat::Str("\"")),
85 LitKind::ByteStr(_, StrStyle::Raw(0)) => (Pat::Str("br\""), Pat::Str("\"")),
86 LitKind::ByteStr(_, StrStyle::Raw(_)) => (Pat::Str("br#\""), Pat::Str("#")),
87 LitKind::Byte(_) => (Pat::Str("b'"), Pat::Str("'")),
88 LitKind::Char(_) => (Pat::Str("'"), Pat::Str("'")),
89 LitKind::Int(_, LitIntType::Signed(IntTy::Isize)) => (Pat::Num, Pat::Str("isize")),
90 LitKind::Int(_, LitIntType::Unsigned(UintTy::Usize)) => (Pat::Num, Pat::Str("usize")),
91 LitKind::Int(..) => (Pat::Num, Pat::Num),
92 LitKind::Float(..) => (Pat::Num, Pat::Str("")),
93 LitKind::Bool(true) => (Pat::Str("true"), Pat::Str("true")),
94 LitKind::Bool(false) => (Pat::Str("false"), Pat::Str("false")),
95 _ => (Pat::Str(""), Pat::Str("")),
96 }
97}
98
99fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) {
101 match path {
102 QPath::Resolved(ty, path) => {
103 let start = if ty.is_some() {
104 Pat::Str("<")
105 } else {
106 path.segments.first().map_or(Pat::Str(""), |seg| {
107 if seg.ident.name == kw::PathRoot {
108 Pat::Str("::")
109 } else {
110 Pat::Sym(seg.ident.name)
111 }
112 })
113 };
114 let end = path.segments.last().map_or(Pat::Str(""), |seg| {
115 if seg.args.is_some() {
116 Pat::Str(">")
117 } else {
118 Pat::Sym(seg.ident.name)
119 }
120 });
121 (start, end)
122 },
123 QPath::TypeRelative(_, name) => (Pat::Str(""), Pat::Sym(name.ident.name)),
124 QPath::LangItem(..) => (Pat::Str(""), Pat::Str("")),
125 }
126}
127
128fn path_search_pat(path: &Path<'_>) -> (Pat, Pat) {
129 let (head, tail) = match path.segments {
130 [] => return (Pat::Str(""), Pat::Str("")),
131 [p] => (Pat::Sym(p.ident.name), p),
132 [.., tail] => (Pat::Str(""), tail),
135 };
136 (
137 head,
138 if tail.args.is_some() {
139 Pat::Str(">")
140 } else {
141 Pat::Sym(tail.ident.name)
142 },
143 )
144}
145
146fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
148 fn expr_search_pat_inner(tcx: TyCtxt<'_>, e: &Expr<'_>, outer_span: Span) -> (Pat, Pat) {
149 if !e.span.eq_ctxt(outer_span) {
155 return (Pat::Str(""), Pat::Str(""));
156 }
157
158 match e.kind {
159 ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")),
160 ExprKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
163 ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat_inner(tcx, e, outer_span).1),
164 ExprKind::Unary(UnOp::Not, e) => (Pat::Str("!"), expr_search_pat_inner(tcx, e, outer_span).1),
165 ExprKind::Unary(UnOp::Neg, e) => (Pat::Str("-"), expr_search_pat_inner(tcx, e, outer_span).1),
166 ExprKind::Lit(lit) => lit_search_pat(&lit.node),
167 ExprKind::Array(_) | ExprKind::Repeat(..) => (Pat::Str("["), Pat::Str("]")),
168 ExprKind::Call(e, []) | ExprKind::MethodCall(_, e, [], _) => {
169 (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("("))
170 },
171 ExprKind::Call(first, [.., last])
172 | ExprKind::MethodCall(_, first, [.., last], _)
173 | ExprKind::Binary(_, first, last)
174 | ExprKind::Tup([first, .., last])
175 | ExprKind::Assign(first, last, _)
176 | ExprKind::AssignOp(_, first, last) => (
177 expr_search_pat_inner(tcx, first, outer_span).0,
178 expr_search_pat_inner(tcx, last, outer_span).1,
179 ),
180 ExprKind::Tup([e]) | ExprKind::DropTemps(e) => expr_search_pat_inner(tcx, e, outer_span),
181 ExprKind::Cast(e, _) | ExprKind::Type(e, _) => (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("")),
182 ExprKind::Let(let_expr) => (Pat::Str("let"), expr_search_pat_inner(tcx, let_expr.init, outer_span).1),
183 ExprKind::If(..) => (Pat::Str("if"), Pat::Str("}")),
184 ExprKind::Loop(_, Some(_), _, _) | ExprKind::Block(_, Some(_)) => (Pat::Str("'"), Pat::Str("}")),
185 ExprKind::Loop(_, None, LoopSource::Loop, _) => (Pat::Str("loop"), Pat::Str("}")),
186 ExprKind::Loop(_, None, LoopSource::While, _) => (Pat::Str("while"), Pat::Str("}")),
187 ExprKind::Loop(_, None, LoopSource::ForLoop, _) | ExprKind::Match(_, _, MatchSource::ForLoopDesugar) => {
188 (Pat::Str("for"), Pat::Str("}"))
189 },
190 ExprKind::Match(_, _, MatchSource::Normal) => (Pat::Str("match"), Pat::Str("}")),
191 ExprKind::Match(e, _, MatchSource::TryDesugar(_)) => {
192 (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("?"))
193 },
194 ExprKind::Match(e, _, MatchSource::AwaitDesugar) | ExprKind::Yield(e, YieldSource::Await { .. }) => {
195 (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("await"))
196 },
197 ExprKind::Closure(&Closure { body, .. }) => (
198 Pat::Str(""),
199 expr_search_pat_inner(tcx, tcx.hir_body(body).value, outer_span).1,
200 ),
201 ExprKind::Block(
202 Block {
203 rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
204 ..
205 },
206 None,
207 ) => (Pat::Str("unsafe"), Pat::Str("}")),
208 ExprKind::Block(_, None) => (Pat::Str("{"), Pat::Str("}")),
209 ExprKind::Field(e, name) => (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Sym(name.name)),
210 ExprKind::Index(e, _, _) => (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("]")),
211 ExprKind::Path(ref path) => qpath_search_pat(path),
212 ExprKind::AddrOf(_, _, e) => (Pat::Str("&"), expr_search_pat_inner(tcx, e, outer_span).1),
213 ExprKind::Break(Destination { label: None, .. }, None) => (Pat::Str("break"), Pat::Str("break")),
214 ExprKind::Break(Destination { label: Some(name), .. }, None) => {
215 (Pat::Str("break"), Pat::Sym(name.ident.name))
216 },
217 ExprKind::Break(_, Some(e)) => (Pat::Str("break"), expr_search_pat_inner(tcx, e, outer_span).1),
218 ExprKind::Continue(Destination { label: None, .. }) => (Pat::Str("continue"), Pat::Str("continue")),
219 ExprKind::Continue(Destination { label: Some(name), .. }) => {
220 (Pat::Str("continue"), Pat::Sym(name.ident.name))
221 },
222 ExprKind::Ret(None) => (Pat::Str("return"), Pat::Str("return")),
223 ExprKind::Ret(Some(e)) => (Pat::Str("return"), expr_search_pat_inner(tcx, e, outer_span).1),
224 ExprKind::Struct(path, _, _) => (qpath_search_pat(path).0, Pat::Str("}")),
225 ExprKind::Yield(e, YieldSource::Yield) => (Pat::Str("yield"), expr_search_pat_inner(tcx, e, outer_span).1),
226 _ => (Pat::Str(""), Pat::Str("")),
227 }
228 }
229
230 expr_search_pat_inner(tcx, e, e.span)
231}
232
233fn fn_header_search_pat(header: FnHeader) -> Pat {
234 if header.is_async() {
235 Pat::Str("async")
236 } else if header.is_const() {
237 Pat::Str("const")
238 } else if header.is_unsafe() {
239 Pat::Str("unsafe")
240 } else if header.abi != ExternAbi::Rust {
241 Pat::Str("extern")
242 } else {
243 Pat::MultiStr(&["fn", "extern"])
244 }
245}
246
247fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) {
248 let (start_pat, end_pat) = match &item.kind {
249 ItemKind::ExternCrate(..) => (Pat::Str("extern"), Pat::Str(";")),
250 ItemKind::Static(..) => (Pat::Str("static"), Pat::Str(";")),
251 ItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
252 ItemKind::Fn { sig, .. } => (fn_header_search_pat(sig.header), Pat::Str("")),
253 ItemKind::ForeignMod { .. } => (Pat::Str("extern"), Pat::Str("}")),
254 ItemKind::TyAlias(..) => (Pat::Str("type"), Pat::Str(";")),
255 ItemKind::Enum(..) => (Pat::Str("enum"), Pat::Str("}")),
256 ItemKind::Struct(_, _, VariantData::Struct { .. }) => (Pat::Str("struct"), Pat::Str("}")),
257 ItemKind::Struct(..) => (Pat::Str("struct"), Pat::Str(";")),
258 ItemKind::Union(..) => (Pat::Str("union"), Pat::Str("}")),
259 ItemKind::Trait(_, _, Safety::Unsafe, ..)
260 | ItemKind::Impl(Impl {
261 of_trait: Some(TraitImplHeader {
262 safety: Safety::Unsafe, ..
263 }),
264 ..
265 }) => (Pat::Str("unsafe"), Pat::Str("}")),
266 ItemKind::Trait(_, IsAuto::Yes, ..) => (Pat::Str("auto"), Pat::Str("}")),
267 ItemKind::Trait(..) => (Pat::Str("trait"), Pat::Str("}")),
268 ItemKind::Impl(_) => (Pat::Str("impl"), Pat::Str("}")),
269 _ => return (Pat::Str(""), Pat::Str("")),
270 };
271 if item.vis_span.is_empty() {
272 (start_pat, end_pat)
273 } else {
274 (Pat::Str("pub"), end_pat)
275 }
276}
277
278fn trait_item_search_pat(item: &TraitItem<'_>) -> (Pat, Pat) {
279 match &item.kind {
280 TraitItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
281 TraitItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")),
282 TraitItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
283 }
284}
285
286fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) {
287 let (mut start_pat, end_pat) = match &item.kind {
288 ImplItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
289 ImplItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")),
290 ImplItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
291 };
292 if let ImplItemImplKind::Inherent { vis_span, .. } = item.impl_kind
293 && !vis_span.is_empty()
294 {
295 start_pat = Pat::Str("pub");
296 }
297 (start_pat, end_pat)
298}
299
300fn field_def_search_pat(def: &FieldDef<'_>) -> (Pat, Pat) {
301 if def.vis_span.is_empty() {
302 if def.is_positional() {
303 (Pat::Str(""), Pat::Str(""))
304 } else {
305 (Pat::Sym(def.ident.name), Pat::Str(""))
306 }
307 } else {
308 (Pat::Str("pub"), Pat::Str(""))
309 }
310}
311
312fn variant_search_pat(v: &Variant<'_>) -> (Pat, Pat) {
313 match v.data {
314 VariantData::Struct { .. } => (Pat::Sym(v.ident.name), Pat::Str("}")),
315 VariantData::Tuple(..) => (Pat::Sym(v.ident.name), Pat::Str("")),
316 VariantData::Unit(..) => (Pat::Sym(v.ident.name), Pat::Sym(v.ident.name)),
317 }
318}
319
320fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirId) -> (Pat, Pat) {
321 let (mut start_pat, end_pat) = match kind {
322 FnKind::ItemFn(.., header) => (fn_header_search_pat(*header), Pat::Str("")),
323 FnKind::Method(.., sig) => (fn_header_search_pat(sig.header), Pat::Str("")),
324 FnKind::Closure => return (Pat::Str(""), expr_search_pat(tcx, body.value).1),
325 };
326 match tcx.hir_node(hir_id) {
327 Node::Item(Item { vis_span, .. })
328 | Node::ImplItem(ImplItem {
329 impl_kind: ImplItemImplKind::Inherent { vis_span, .. },
330 ..
331 }) => {
332 if !vis_span.is_empty() {
333 start_pat = Pat::Str("pub");
334 }
335 },
336 Node::ImplItem(_) | Node::TraitItem(_) => {},
337 _ => start_pat = Pat::Str(""),
338 }
339 (start_pat, end_pat)
340}
341
342fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
343 match attr.kind {
344 AttrKind::Normal(..) => {
345 if let Some(ident) = attr.ident() {
346 let ident_string = ident.to_string();
348 if attr.style == AttrStyle::Outer {
349 (
350 Pat::OwnedMultiStr(vec!["#[".to_owned() + &ident_string, ident_string]),
351 Pat::Str(""),
352 )
353 } else {
354 (
355 Pat::OwnedMultiStr(vec!["#![".to_owned() + &ident_string, ident_string]),
356 Pat::Str(""),
357 )
358 }
359 } else {
360 (Pat::Str("#"), Pat::Str("]"))
361 }
362 },
363 AttrKind::DocComment(_kind @ CommentKind::Line, ..) => {
364 if attr.style == AttrStyle::Outer {
365 (Pat::Str("///"), Pat::Str(""))
366 } else {
367 (Pat::Str("//!"), Pat::Str(""))
368 }
369 },
370 AttrKind::DocComment(_kind @ CommentKind::Block, ..) => {
371 if attr.style == AttrStyle::Outer {
372 (Pat::Str("/**"), Pat::Str("*/"))
373 } else {
374 (Pat::Str("/*!"), Pat::Str("*/"))
375 }
376 },
377 }
378}
379
380fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
381 match ty.kind {
382 TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")),
383 TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ty_search_pat(ty).1),
384 TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ty_search_pat(ty).1),
385 TyKind::FnPtr(fn_ptr) => (
386 if fn_ptr.safety.is_unsafe() {
387 Pat::Str("unsafe")
388 } else if fn_ptr.abi != ExternAbi::Rust {
389 Pat::Str("extern")
390 } else {
391 Pat::MultiStr(&["fn", "extern"])
392 },
393 match fn_ptr.decl.output {
394 FnRetTy::DefaultReturn(_) => {
395 if let [.., ty] = fn_ptr.decl.inputs {
396 ty_search_pat(ty).1
397 } else {
398 Pat::Str("(")
399 }
400 },
401 FnRetTy::Return(ty) => ty_search_pat(ty).1,
402 },
403 ),
404 TyKind::Never => (Pat::Str("!"), Pat::Str("!")),
405 TyKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
408 TyKind::Tup([ty]) => ty_search_pat(ty),
409 TyKind::Tup([head, .., tail]) => (ty_search_pat(head).0, ty_search_pat(tail).1),
410 TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")),
411 TyKind::Path(qpath) => qpath_search_pat(&qpath),
412 TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")),
413 TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ty_search_pat(binder_ty.inner_ty).1),
414 TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => {
415 (Pat::Str("dyn"), Pat::Str(""))
416 },
417 _ => (Pat::Str(""), Pat::Str("")),
419 }
420}
421
422fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) {
423 use ast::{Extern, FnRetTy, MutTy, Safety, TraitObjectSyntax, TyKind};
424
425 match &ty.kind {
426 TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")),
427 TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ast_ty_search_pat(ty).1),
428 TyKind::Ref(_, MutTy { ty, .. }) | TyKind::PinnedRef(_, MutTy { ty, .. }) => {
429 (Pat::Str("&"), ast_ty_search_pat(ty).1)
430 },
431 TyKind::FnPtr(fn_ptr) => (
432 if let Safety::Unsafe(_) = fn_ptr.safety {
433 Pat::Str("unsafe")
434 } else if let Extern::Explicit(strlit, _) = fn_ptr.ext
435 && strlit.symbol == sym::rust
436 {
437 Pat::MultiStr(&["fn", "extern"])
438 } else {
439 Pat::Str("extern")
440 },
441 match &fn_ptr.decl.output {
442 FnRetTy::Default(_) => {
443 if let [.., param] = &*fn_ptr.decl.inputs {
444 ast_ty_search_pat(¶m.ty).1
445 } else {
446 Pat::Str("(")
447 }
448 },
449 FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1,
450 },
451 ),
452 TyKind::Never => (Pat::Str("!"), Pat::Str("!")),
453 TyKind::Tup(tup) => match &**tup {
456 [] => (Pat::Str(")"), Pat::Str("(")),
457 [ty] => ast_ty_search_pat(ty),
458 [head, .., tail] => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1),
459 },
460 TyKind::ImplTrait(..) => (Pat::Str("impl"), Pat::Str("")),
461 TyKind::Path(qself_path, path) => {
462 let start = if qself_path.is_some() {
463 Pat::Str("<")
464 } else if let Some(first) = path.segments.first() {
465 ident_search_pat(first.ident).0
466 } else {
467 Pat::Str("")
469 };
470 let end = if let Some(last) = path.segments.last() {
471 match last.args.as_deref() {
472 Some(GenericArgs::AngleBracketed(_)) => Pat::Str(">"),
474 Some(GenericArgs::Parenthesized(par_args)) => match &par_args.output {
475 FnRetTy::Default(_) => {
476 if let Some(last) = par_args.inputs.last() {
477 ast_ty_search_pat(last).1
479 } else {
480 Pat::Str("(")
482 }
483 },
484 FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1,
486 },
487 Some(GenericArgs::ParenthesizedElided(_)) => Pat::Str(".."),
489 None => ident_search_pat(last.ident).1,
491 }
492 } else {
493 #[allow(
495 clippy::collapsible_else_if,
496 reason = "we want to keep these cases together, since they are both impossible"
497 )]
498 if qself_path.is_some() {
499 Pat::Str(">")
501 } else {
502 Pat::Str("")
503 }
504 };
505 (start, end)
506 },
507 TyKind::Infer => (Pat::Str("_"), Pat::Str("_")),
508 TyKind::Paren(ty) => ast_ty_search_pat(ty),
509 TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ast_ty_search_pat(&binder_ty.inner_ty).1),
510 TyKind::TraitObject(_, trait_obj_syntax) => {
511 if let TraitObjectSyntax::Dyn = trait_obj_syntax {
512 (Pat::Str("dyn"), Pat::Str(""))
513 } else {
514 (Pat::Str(""), Pat::Str(""))
516 }
517 },
518 TyKind::MacCall(mac_call) => {
519 let start = if let Some(first) = mac_call.path.segments.first() {
520 ident_search_pat(first.ident).0
521 } else {
522 Pat::Str("")
523 };
524 (start, Pat::Str(""))
525 },
526
527 TyKind::ImplicitSelf
529
530 |TyKind::Pat(..)
532
533 | TyKind::CVarArgs
535 | TyKind::Typeof(_)
536
537 | TyKind::Dummy
539 | TyKind::Err(_) => (Pat::Str(""), Pat::Str("")),
540 }
541}
542
543fn ident_search_pat(ident: Ident) -> (Pat, Pat) {
544 (Pat::Sym(ident.name), Pat::Sym(ident.name))
545}
546
547pub trait WithSearchPat<'cx> {
548 type Context: LintContext;
549 fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat);
550 fn span(&self) -> Span;
551}
552macro_rules! impl_with_search_pat {
553 (($cx_ident:ident: $cx_ty:ident<$cx_lt:lifetime>, $self:tt: $ty:ty) => $fn:ident($($args:tt)*)) => {
554 impl<$cx_lt> WithSearchPat<$cx_lt> for $ty {
555 type Context = $cx_ty<$cx_lt>;
556 fn search_pat(&$self, $cx_ident: &Self::Context) -> (Pat, Pat) {
557 $fn($($args)*)
558 }
559 fn span(&self) -> Span {
560 self.span
561 }
562 }
563 };
564}
565impl_with_search_pat!((cx: LateContext<'tcx>, self: Expr<'tcx>) => expr_search_pat(cx.tcx, self));
566impl_with_search_pat!((_cx: LateContext<'tcx>, self: Item<'_>) => item_search_pat(self));
567impl_with_search_pat!((_cx: LateContext<'tcx>, self: TraitItem<'_>) => trait_item_search_pat(self));
568impl_with_search_pat!((_cx: LateContext<'tcx>, self: ImplItem<'_>) => impl_item_search_pat(self));
569impl_with_search_pat!((_cx: LateContext<'tcx>, self: FieldDef<'_>) => field_def_search_pat(self));
570impl_with_search_pat!((_cx: LateContext<'tcx>, self: Variant<'_>) => variant_search_pat(self));
571impl_with_search_pat!((_cx: LateContext<'tcx>, self: Ty<'_>) => ty_search_pat(self));
572impl_with_search_pat!((_cx: LateContext<'tcx>, self: Ident) => ident_search_pat(*self));
573impl_with_search_pat!((_cx: LateContext<'tcx>, self: Lit) => lit_search_pat(&self.node));
574impl_with_search_pat!((_cx: LateContext<'tcx>, self: Path<'_>) => path_search_pat(self));
575
576impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: Attribute) => attr_search_pat(self));
577impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: ast::Ty) => ast_ty_search_pat(self));
578
579impl<'cx> WithSearchPat<'cx> for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
580 type Context = LateContext<'cx>;
581
582 fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat) {
583 fn_kind_pat(cx.tcx, self.0, self.1, self.2)
584 }
585
586 fn span(&self) -> Span {
587 self.3
588 }
589}
590
591pub fn is_from_proc_macro<'cx, T: WithSearchPat<'cx>>(cx: &T::Context, item: &T) -> bool {
596 let (start_pat, end_pat) = item.search_pat(cx);
597 !span_matches_pat(cx.sess(), item.span(), start_pat, end_pat)
598}
599
600pub fn is_span_match(cx: &impl LintContext, span: Span) -> bool {
602 span_matches_pat(cx.sess(), span, Pat::Str("match"), Pat::Str("}"))
603}
604
605pub fn is_span_if(cx: &impl LintContext, span: Span) -> bool {
607 span_matches_pat(cx.sess(), span, Pat::Str("if"), Pat::Str("}"))
608}