1use rustc_hir::def::Res;
5use rustc_hir::def_id::DefId;
6use rustc_hir::{Expr, ExprKind, HirId};
7use rustc_middle::ty::{self, ClauseKind, GenericArgsRef, PredicatePolarity, TraitPredicate, Ty};
8use rustc_session::{declare_lint_pass, declare_tool_lint};
9use rustc_span::hygiene::{ExpnKind, MacroKind};
10use rustc_span::{Span, sym};
11use tracing::debug;
12use {rustc_ast as ast, rustc_hir as hir};
13
14use crate::lints::{
15 BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand,
16 NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag,
17 SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrDirectUse,
18 TypeIrInherentUsage, TypeIrTraitUsage, UntranslatableDiag,
19};
20use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
21
22declare_tool_lint! {
23 pub rustc::DEFAULT_HASH_TYPES,
29 Allow,
30 "forbid HashMap and HashSet and suggest the FxHash* variants",
31 report_in_external_macro: true
32}
33
34declare_lint_pass!(DefaultHashTypes => [DEFAULT_HASH_TYPES]);
35
36impl LateLintPass<'_> for DefaultHashTypes {
37 fn check_path(&mut self, cx: &LateContext<'_>, path: &hir::Path<'_>, hir_id: HirId) {
38 let Res::Def(rustc_hir::def::DefKind::Struct, def_id) = path.res else { return };
39 if matches!(
40 cx.tcx.hir_node(hir_id),
41 hir::Node::Item(hir::Item { kind: hir::ItemKind::Use(..), .. })
42 ) {
43 return;
45 }
46 let preferred = match cx.tcx.get_diagnostic_name(def_id) {
47 Some(sym::HashMap) => "FxHashMap",
48 Some(sym::HashSet) => "FxHashSet",
49 _ => return,
50 };
51 cx.emit_span_lint(
52 DEFAULT_HASH_TYPES,
53 path.span,
54 DefaultHashTypesDiag { preferred, used: cx.tcx.item_name(def_id) },
55 );
56 }
57}
58
59declare_tool_lint! {
60 pub rustc::POTENTIAL_QUERY_INSTABILITY,
67 Allow,
68 "require explicit opt-in when using potentially unstable methods or functions",
69 report_in_external_macro: true
70}
71
72declare_tool_lint! {
73 pub rustc::UNTRACKED_QUERY_INFORMATION,
78 Allow,
79 "require explicit opt-in when accessing information not tracked by the query system",
80 report_in_external_macro: true
81}
82
83declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY, UNTRACKED_QUERY_INFORMATION]);
84
85impl<'tcx> LateLintPass<'tcx> for QueryStability {
86 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
87 if let Some((callee_def_id, span, generic_args, _recv, _args)) =
88 get_callee_span_generic_args_and_args(cx, expr)
89 && let Ok(Some(instance)) =
90 ty::Instance::try_resolve(cx.tcx, cx.typing_env(), callee_def_id, generic_args)
91 {
92 let def_id = instance.def_id();
93 if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
94 cx.emit_span_lint(
95 POTENTIAL_QUERY_INSTABILITY,
96 span,
97 QueryInstability { query: cx.tcx.item_name(def_id) },
98 );
99 } else if has_unstable_into_iter_predicate(cx, callee_def_id, generic_args) {
100 let call_span = span.with_hi(expr.span.hi());
101 cx.emit_span_lint(
102 POTENTIAL_QUERY_INSTABILITY,
103 call_span,
104 QueryInstability { query: sym::into_iter },
105 );
106 }
107
108 if cx.tcx.has_attr(def_id, sym::rustc_lint_untracked_query_information) {
109 cx.emit_span_lint(
110 UNTRACKED_QUERY_INFORMATION,
111 span,
112 QueryUntracked { method: cx.tcx.item_name(def_id) },
113 );
114 }
115 }
116 }
117}
118
119fn has_unstable_into_iter_predicate<'tcx>(
120 cx: &LateContext<'tcx>,
121 callee_def_id: DefId,
122 generic_args: GenericArgsRef<'tcx>,
123) -> bool {
124 let Some(into_iterator_def_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) else {
125 return false;
126 };
127 let Some(into_iter_fn_def_id) = cx.tcx.lang_items().into_iter_fn() else {
128 return false;
129 };
130 let predicates = cx.tcx.predicates_of(callee_def_id).instantiate(cx.tcx, generic_args);
131 for (predicate, _) in predicates {
132 let ClauseKind::Trait(TraitPredicate { trait_ref, polarity: PredicatePolarity::Positive }) =
133 predicate.kind().skip_binder()
134 else {
135 continue;
136 };
137 if trait_ref.def_id != into_iterator_def_id {
139 continue;
140 }
141 let Ok(Some(instance)) =
142 ty::Instance::try_resolve(cx.tcx, cx.typing_env(), into_iter_fn_def_id, trait_ref.args)
143 else {
144 continue;
145 };
146 if cx.tcx.has_attr(instance.def_id(), sym::rustc_lint_query_instability) {
149 return true;
150 }
151 }
152 false
153}
154
155fn get_callee_span_generic_args_and_args<'tcx>(
159 cx: &LateContext<'tcx>,
160 expr: &'tcx Expr<'tcx>,
161) -> Option<(DefId, Span, GenericArgsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> {
162 if let ExprKind::Call(callee, args) = expr.kind
163 && let callee_ty = cx.typeck_results().expr_ty(callee)
164 && let ty::FnDef(callee_def_id, generic_args) = callee_ty.kind()
165 {
166 return Some((*callee_def_id, callee.span, generic_args, None, args));
167 }
168 if let ExprKind::MethodCall(segment, recv, args, _) = expr.kind
169 && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
170 {
171 let generic_args = cx.typeck_results().node_args(expr.hir_id);
172 return Some((method_def_id, segment.ident.span, generic_args, Some(recv), args));
173 }
174 None
175}
176
177declare_tool_lint! {
178 pub rustc::USAGE_OF_TY_TYKIND,
181 Allow,
182 "usage of `ty::TyKind` outside of the `ty::sty` module",
183 report_in_external_macro: true
184}
185
186declare_tool_lint! {
187 pub rustc::USAGE_OF_QUALIFIED_TY,
190 Allow,
191 "using `ty::{Ty,TyCtxt}` instead of importing it",
192 report_in_external_macro: true
193}
194
195declare_lint_pass!(TyTyKind => [
196 USAGE_OF_TY_TYKIND,
197 USAGE_OF_QUALIFIED_TY,
198]);
199
200impl<'tcx> LateLintPass<'tcx> for TyTyKind {
201 fn check_path(
202 &mut self,
203 cx: &LateContext<'tcx>,
204 path: &rustc_hir::Path<'tcx>,
205 _: rustc_hir::HirId,
206 ) {
207 if let Some(segment) = path.segments.iter().nth_back(1)
208 && lint_ty_kind_usage(cx, &segment.res)
209 {
210 let span =
211 path.span.with_hi(segment.args.map_or(segment.ident.span, |a| a.span_ext).hi());
212 cx.emit_span_lint(USAGE_OF_TY_TYKIND, path.span, TykindKind { suggestion: span });
213 }
214 }
215
216 fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) {
217 match &ty.kind {
218 hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
219 if lint_ty_kind_usage(cx, &path.res) {
220 let span = match cx.tcx.parent_hir_node(ty.hir_id) {
221 hir::Node::PatExpr(hir::PatExpr {
222 kind: hir::PatExprKind::Path(qpath),
223 ..
224 })
225 | hir::Node::Pat(hir::Pat {
226 kind:
227 hir::PatKind::TupleStruct(qpath, ..) | hir::PatKind::Struct(qpath, ..),
228 ..
229 })
230 | hir::Node::Expr(
231 hir::Expr { kind: hir::ExprKind::Path(qpath), .. }
232 | &hir::Expr { kind: hir::ExprKind::Struct(qpath, ..), .. },
233 ) => {
234 if let hir::QPath::TypeRelative(qpath_ty, ..) = qpath
235 && qpath_ty.hir_id == ty.hir_id
236 {
237 Some(path.span)
238 } else {
239 None
240 }
241 }
242 _ => None,
243 };
244
245 match span {
246 Some(span) => {
247 cx.emit_span_lint(
248 USAGE_OF_TY_TYKIND,
249 path.span,
250 TykindKind { suggestion: span },
251 );
252 }
253 None => cx.emit_span_lint(USAGE_OF_TY_TYKIND, path.span, TykindDiag),
254 }
255 } else if !ty.span.from_expansion()
256 && path.segments.len() > 1
257 && let Some(ty) = is_ty_or_ty_ctxt(cx, path)
258 {
259 cx.emit_span_lint(
260 USAGE_OF_QUALIFIED_TY,
261 path.span,
262 TyQualified { ty, suggestion: path.span },
263 );
264 }
265 }
266 _ => {}
267 }
268 }
269}
270
271fn lint_ty_kind_usage(cx: &LateContext<'_>, res: &Res) -> bool {
272 if let Some(did) = res.opt_def_id() {
273 cx.tcx.is_diagnostic_item(sym::TyKind, did) || cx.tcx.is_diagnostic_item(sym::IrTyKind, did)
274 } else {
275 false
276 }
277}
278
279fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &hir::Path<'_>) -> Option<String> {
280 match &path.res {
281 Res::Def(_, def_id) => {
282 if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(*def_id) {
283 return Some(format!("{}{}", name, gen_args(path.segments.last().unwrap())));
284 }
285 }
286 Res::SelfTyAlias { alias_to: did, is_trait_impl: false, .. } => {
288 if let ty::Adt(adt, args) = cx.tcx.type_of(did).instantiate_identity().kind()
289 && let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(adt.did())
290 {
291 return Some(format!("{}<{}>", name, args[0]));
292 }
293 }
294 _ => (),
295 }
296
297 None
298}
299
300fn gen_args(segment: &hir::PathSegment<'_>) -> String {
301 if let Some(args) = &segment.args {
302 let lifetimes = args
303 .args
304 .iter()
305 .filter_map(|arg| {
306 if let hir::GenericArg::Lifetime(lt) = arg {
307 Some(lt.ident.to_string())
308 } else {
309 None
310 }
311 })
312 .collect::<Vec<_>>();
313
314 if !lifetimes.is_empty() {
315 return format!("<{}>", lifetimes.join(", "));
316 }
317 }
318
319 String::new()
320}
321
322declare_tool_lint! {
323 pub rustc::NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT,
326 Allow,
327 "non-glob import of `rustc_type_ir::inherent`",
328 report_in_external_macro: true
329}
330
331declare_tool_lint! {
332 pub rustc::USAGE_OF_TYPE_IR_INHERENT,
336 Allow,
337 "usage `rustc_type_ir::inherent` outside of trait system",
338 report_in_external_macro: true
339}
340
341declare_tool_lint! {
342 pub rustc::USAGE_OF_TYPE_IR_TRAITS,
349 Allow,
350 "usage `rustc_type_ir`-specific abstraction traits outside of trait system",
351 report_in_external_macro: true
352}
353declare_tool_lint! {
354 pub rustc::DIRECT_USE_OF_RUSTC_TYPE_IR,
359 Allow,
360 "usage `rustc_type_ir` abstraction outside of trait system",
361 report_in_external_macro: true
362}
363
364declare_lint_pass!(TypeIr => [DIRECT_USE_OF_RUSTC_TYPE_IR, NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_TRAITS]);
365
366impl<'tcx> LateLintPass<'tcx> for TypeIr {
367 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
368 let res_def_id = match expr.kind {
369 hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => path.res.opt_def_id(),
370 hir::ExprKind::Path(hir::QPath::TypeRelative(..)) | hir::ExprKind::MethodCall(..) => {
371 cx.typeck_results().type_dependent_def_id(expr.hir_id)
372 }
373 _ => return,
374 };
375 let Some(res_def_id) = res_def_id else {
376 return;
377 };
378 if let Some(assoc_item) = cx.tcx.opt_associated_item(res_def_id)
379 && let Some(trait_def_id) = assoc_item.trait_container(cx.tcx)
380 && (cx.tcx.is_diagnostic_item(sym::type_ir_interner, trait_def_id)
381 | cx.tcx.is_diagnostic_item(sym::type_ir_infer_ctxt_like, trait_def_id))
382 {
383 cx.emit_span_lint(USAGE_OF_TYPE_IR_TRAITS, expr.span, TypeIrTraitUsage);
384 }
385 }
386
387 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
388 let rustc_hir::ItemKind::Use(path, kind) = item.kind else { return };
389
390 let is_mod_inherent = |res: Res| {
391 res.opt_def_id()
392 .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id))
393 };
394
395 if let Some(seg) = path.segments.iter().find(|seg| is_mod_inherent(seg.res)) {
397 cx.emit_span_lint(USAGE_OF_TYPE_IR_INHERENT, seg.ident.span, TypeIrInherentUsage);
398 }
399 else if let Some(type_ns) = path.res.type_ns
401 && is_mod_inherent(type_ns)
402 {
403 cx.emit_span_lint(
404 USAGE_OF_TYPE_IR_INHERENT,
405 path.segments.last().unwrap().ident.span,
406 TypeIrInherentUsage,
407 );
408 }
409
410 let (lo, hi, snippet) = match path.segments {
411 [.., penultimate, segment] if is_mod_inherent(penultimate.res) => {
412 (segment.ident.span, item.kind.ident().unwrap().span, "*")
413 }
414 [.., segment]
415 if let Some(type_ns) = path.res.type_ns
416 && is_mod_inherent(type_ns)
417 && let rustc_hir::UseKind::Single(ident) = kind =>
418 {
419 let (lo, snippet) =
420 match cx.tcx.sess.source_map().span_to_snippet(path.span).as_deref() {
421 Ok("self") => (path.span, "*"),
422 _ => (segment.ident.span.shrink_to_hi(), "::*"),
423 };
424 (lo, if segment.ident == ident { lo } else { ident.span }, snippet)
425 }
426 _ => return,
427 };
428 cx.emit_span_lint(
429 NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT,
430 path.span,
431 NonGlobImportTypeIrInherent { suggestion: lo.eq_ctxt(hi).then(|| lo.to(hi)), snippet },
432 );
433 }
434
435 fn check_path(
436 &mut self,
437 cx: &LateContext<'tcx>,
438 path: &rustc_hir::Path<'tcx>,
439 _: rustc_hir::HirId,
440 ) {
441 if let Some(seg) = path.segments.iter().find(|seg| {
442 seg.res
443 .opt_def_id()
444 .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::type_ir, def_id))
445 }) {
446 cx.emit_span_lint(DIRECT_USE_OF_RUSTC_TYPE_IR, seg.ident.span, TypeIrDirectUse);
447 }
448 }
449}
450
451declare_tool_lint! {
452 pub rustc::LINT_PASS_IMPL_WITHOUT_MACRO,
455 Allow,
456 "`impl LintPass` without the `declare_lint_pass!` or `impl_lint_pass!` macros"
457}
458
459declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]);
460
461impl EarlyLintPass for LintPassImpl {
462 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
463 if let ast::ItemKind::Impl(ast::Impl { of_trait: Some(of_trait), .. }) = &item.kind
464 && let Some(last) = of_trait.trait_ref.path.segments.last()
465 && last.ident.name == sym::LintPass
466 {
467 let expn_data = of_trait.trait_ref.path.span.ctxt().outer_expn_data();
468 let call_site = expn_data.call_site;
469 if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass)
470 && call_site.ctxt().outer_expn_data().kind
471 != ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass)
472 {
473 cx.emit_span_lint(
474 LINT_PASS_IMPL_WITHOUT_MACRO,
475 of_trait.trait_ref.path.span,
476 LintPassByHand,
477 );
478 }
479 }
480 }
481}
482
483declare_tool_lint! {
484 pub rustc::UNTRANSLATABLE_DIAGNOSTIC,
490 Allow,
491 "prevent creation of diagnostics which cannot be translated",
492 report_in_external_macro: true,
493 @eval_always = true
494}
495
496declare_tool_lint! {
497 pub rustc::DIAGNOSTIC_OUTSIDE_OF_IMPL,
504 Allow,
505 "prevent diagnostic creation outside of `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls",
506 report_in_external_macro: true,
507 @eval_always = true
508}
509
510declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]);
511
512impl LateLintPass<'_> for Diagnostics {
513 fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
514 let collect_args_tys_and_spans = |args: &[hir::Expr<'_>], reserve_one_extra: bool| {
515 let mut result = Vec::with_capacity(args.len() + usize::from(reserve_one_extra));
516 result.extend(args.iter().map(|arg| (cx.typeck_results().expr_ty(arg), arg.span)));
517 result
518 };
519 let Some((def_id, span, fn_gen_args, recv, args)) =
521 get_callee_span_generic_args_and_args(cx, expr)
522 else {
523 return;
524 };
525 let mut arg_tys_and_spans = collect_args_tys_and_spans(args, recv.is_some());
526 if let Some(recv) = recv {
527 arg_tys_and_spans.insert(0, (cx.tcx.types.self_param, recv.span)); }
529
530 Self::diagnostic_outside_of_impl(cx, span, expr.hir_id, def_id, fn_gen_args);
531 Self::untranslatable_diagnostic(cx, def_id, &arg_tys_and_spans);
532 }
533}
534
535impl Diagnostics {
536 fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: Ty<'cx>) -> bool {
538 if let Some(adt_def) = ty.ty_adt_def()
539 && let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did())
540 && matches!(name, sym::DiagMessage | sym::SubdiagMessage)
541 {
542 true
543 } else {
544 false
545 }
546 }
547
548 fn untranslatable_diagnostic<'cx>(
549 cx: &LateContext<'cx>,
550 def_id: DefId,
551 arg_tys_and_spans: &[(Ty<'cx>, Span)],
552 ) {
553 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
554 let predicates = cx.tcx.predicates_of(def_id).instantiate_identity(cx.tcx).predicates;
555 for (i, ¶m_ty) in fn_sig.inputs().iter().enumerate() {
556 if let ty::Param(sig_param) = param_ty.kind() {
557 for pred in predicates.iter() {
559 if let Some(trait_pred) = pred.as_trait_clause()
560 && let trait_ref = trait_pred.skip_binder().trait_ref
561 && trait_ref.self_ty() == param_ty && cx.tcx.is_diagnostic_item(sym::Into, trait_ref.def_id)
563 && let ty1 = trait_ref.args.type_at(1)
564 && Self::is_diag_message(cx, ty1)
565 {
566 let (arg_ty, arg_span) = arg_tys_and_spans[i];
570
571 let is_translatable = Self::is_diag_message(cx, arg_ty)
573 || matches!(arg_ty.kind(), ty::Param(arg_param) if arg_param.name == sig_param.name);
574 if !is_translatable {
575 cx.emit_span_lint(
576 UNTRANSLATABLE_DIAGNOSTIC,
577 arg_span,
578 UntranslatableDiag,
579 );
580 }
581 }
582 }
583 }
584 }
585 }
586
587 fn diagnostic_outside_of_impl<'cx>(
588 cx: &LateContext<'cx>,
589 span: Span,
590 current_id: HirId,
591 def_id: DefId,
592 fn_gen_args: GenericArgsRef<'cx>,
593 ) {
594 let Some(inst) =
596 ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, fn_gen_args).ok().flatten()
597 else {
598 return;
599 };
600 let has_attr = cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics);
601 if !has_attr {
602 return;
603 };
604
605 for (hir_id, _parent) in cx.tcx.hir_parent_iter(current_id) {
606 if let Some(owner_did) = hir_id.as_owner()
607 && cx.tcx.has_attr(owner_did, sym::rustc_lint_diagnostics)
608 {
609 return;
611 }
612 }
613
614 let mut is_inside_appropriate_impl = false;
620 for (_hir_id, parent) in cx.tcx.hir_parent_iter(current_id) {
621 debug!(?parent);
622 if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) = parent
623 && let Some(of_trait) = impl_.of_trait
624 && let Some(def_id) = of_trait.trait_ref.trait_def_id()
625 && let Some(name) = cx.tcx.get_diagnostic_name(def_id)
626 && matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic)
627 {
628 is_inside_appropriate_impl = true;
629 break;
630 }
631 }
632 debug!(?is_inside_appropriate_impl);
633 if !is_inside_appropriate_impl {
634 cx.emit_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl);
635 }
636 }
637}
638
639declare_tool_lint! {
640 pub rustc::BAD_OPT_ACCESS,
643 Deny,
644 "prevent using options by field access when there is a wrapper function",
645 report_in_external_macro: true
646}
647
648declare_lint_pass!(BadOptAccess => [BAD_OPT_ACCESS]);
649
650impl LateLintPass<'_> for BadOptAccess {
651 fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
652 let hir::ExprKind::Field(base, target) = expr.kind else { return };
653 let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return };
654 if !cx.tcx.has_attr(adt_def.did(), sym::rustc_lint_opt_ty) {
657 return;
658 }
659
660 for field in adt_def.all_fields() {
661 if field.name == target.name
662 && let Some(attr) =
663 cx.tcx.get_attr(field.did, sym::rustc_lint_opt_deny_field_access)
664 && let Some(items) = attr.meta_item_list()
665 && let Some(item) = items.first()
666 && let Some(lit) = item.lit()
667 && let ast::LitKind::Str(val, _) = lit.kind
668 {
669 cx.emit_span_lint(
670 BAD_OPT_ACCESS,
671 expr.span,
672 BadOptAccessDiag { msg: val.as_str() },
673 );
674 }
675 }
676 }
677}
678
679declare_tool_lint! {
680 pub rustc::SPAN_USE_EQ_CTXT,
681 Allow,
682 "forbid uses of `==` with `Span::ctxt`, suggest `Span::eq_ctxt` instead",
683 report_in_external_macro: true
684}
685
686declare_lint_pass!(SpanUseEqCtxt => [SPAN_USE_EQ_CTXT]);
687
688impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt {
689 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
690 if let hir::ExprKind::Binary(
691 hir::BinOp { node: hir::BinOpKind::Eq | hir::BinOpKind::Ne, .. },
692 lhs,
693 rhs,
694 ) = expr.kind
695 {
696 if is_span_ctxt_call(cx, lhs) && is_span_ctxt_call(cx, rhs) {
697 cx.emit_span_lint(SPAN_USE_EQ_CTXT, expr.span, SpanUseEqCtxtDiag);
698 }
699 }
700 }
701}
702
703fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
704 match &expr.kind {
705 hir::ExprKind::MethodCall(..) => cx
706 .typeck_results()
707 .type_dependent_def_id(expr.hir_id)
708 .is_some_and(|call_did| cx.tcx.is_diagnostic_item(sym::SpanCtxt, call_did)),
709
710 _ => false,
711 }
712}
713
714declare_tool_lint! {
715 pub rustc::SYMBOL_INTERN_STRING_LITERAL,
717 Allow,
720 "Forbid uses of string literals in `Symbol::intern`, suggesting preinterning instead",
721 report_in_external_macro: true
722}
723
724declare_lint_pass!(SymbolInternStringLiteral => [SYMBOL_INTERN_STRING_LITERAL]);
725
726impl<'tcx> LateLintPass<'tcx> for SymbolInternStringLiteral {
727 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
728 if let hir::ExprKind::Call(path, [arg]) = expr.kind
729 && let hir::ExprKind::Path(ref qpath) = path.kind
730 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
731 && cx.tcx.is_diagnostic_item(sym::SymbolIntern, def_id)
732 && let hir::ExprKind::Lit(kind) = arg.kind
733 && let rustc_ast::LitKind::Str(_, _) = kind.node
734 {
735 cx.emit_span_lint(
736 SYMBOL_INTERN_STRING_LITERAL,
737 kind.span,
738 SymbolInternStringLiteralDiag,
739 );
740 }
741 }
742}