1use rustc_hir::HirId;
5use rustc_hir::def::Res;
6use rustc_hir::def_id::DefId;
7use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy};
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
59fn typeck_results_of_method_fn<'tcx>(
62 cx: &LateContext<'tcx>,
63 expr: &hir::Expr<'_>,
64) -> Option<(Span, DefId, ty::GenericArgsRef<'tcx>)> {
65 match expr.kind {
66 hir::ExprKind::MethodCall(segment, ..)
67 if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
68 {
69 Some((segment.ident.span, def_id, cx.typeck_results().node_args(expr.hir_id)))
70 }
71 _ => match cx.typeck_results().node_type(expr.hir_id).kind() {
72 &ty::FnDef(def_id, args) => Some((expr.span, def_id, args)),
73 _ => None,
74 },
75 }
76}
77
78declare_tool_lint! {
79 pub rustc::POTENTIAL_QUERY_INSTABILITY,
86 Allow,
87 "require explicit opt-in when using potentially unstable methods or functions",
88 report_in_external_macro: true
89}
90
91declare_tool_lint! {
92 pub rustc::UNTRACKED_QUERY_INFORMATION,
97 Allow,
98 "require explicit opt-in when accessing information not tracked by the query system",
99 report_in_external_macro: true
100}
101
102declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY, UNTRACKED_QUERY_INFORMATION]);
103
104impl LateLintPass<'_> for QueryStability {
105 fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
106 let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return };
107 if let Ok(Some(instance)) = ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, args)
108 {
109 let def_id = instance.def_id();
110 if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
111 cx.emit_span_lint(
112 POTENTIAL_QUERY_INSTABILITY,
113 span,
114 QueryInstability { query: cx.tcx.item_name(def_id) },
115 );
116 }
117 if cx.tcx.has_attr(def_id, sym::rustc_lint_untracked_query_information) {
118 cx.emit_span_lint(
119 UNTRACKED_QUERY_INFORMATION,
120 span,
121 QueryUntracked { method: cx.tcx.item_name(def_id) },
122 );
123 }
124 }
125 }
126}
127
128declare_tool_lint! {
129 pub rustc::USAGE_OF_TY_TYKIND,
132 Allow,
133 "usage of `ty::TyKind` outside of the `ty::sty` module",
134 report_in_external_macro: true
135}
136
137declare_tool_lint! {
138 pub rustc::USAGE_OF_QUALIFIED_TY,
141 Allow,
142 "using `ty::{Ty,TyCtxt}` instead of importing it",
143 report_in_external_macro: true
144}
145
146declare_lint_pass!(TyTyKind => [
147 USAGE_OF_TY_TYKIND,
148 USAGE_OF_QUALIFIED_TY,
149]);
150
151impl<'tcx> LateLintPass<'tcx> for TyTyKind {
152 fn check_path(
153 &mut self,
154 cx: &LateContext<'tcx>,
155 path: &rustc_hir::Path<'tcx>,
156 _: rustc_hir::HirId,
157 ) {
158 if let Some(segment) = path.segments.iter().nth_back(1)
159 && lint_ty_kind_usage(cx, &segment.res)
160 {
161 let span =
162 path.span.with_hi(segment.args.map_or(segment.ident.span, |a| a.span_ext).hi());
163 cx.emit_span_lint(USAGE_OF_TY_TYKIND, path.span, TykindKind { suggestion: span });
164 }
165 }
166
167 fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) {
168 match &ty.kind {
169 hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
170 if lint_ty_kind_usage(cx, &path.res) {
171 let span = match cx.tcx.parent_hir_node(ty.hir_id) {
172 hir::Node::PatExpr(hir::PatExpr {
173 kind: hir::PatExprKind::Path(qpath),
174 ..
175 })
176 | hir::Node::Pat(hir::Pat {
177 kind:
178 hir::PatKind::TupleStruct(qpath, ..) | hir::PatKind::Struct(qpath, ..),
179 ..
180 })
181 | hir::Node::Expr(
182 hir::Expr { kind: hir::ExprKind::Path(qpath), .. }
183 | &hir::Expr { kind: hir::ExprKind::Struct(qpath, ..), .. },
184 ) => {
185 if let hir::QPath::TypeRelative(qpath_ty, ..) = qpath
186 && qpath_ty.hir_id == ty.hir_id
187 {
188 Some(path.span)
189 } else {
190 None
191 }
192 }
193 _ => None,
194 };
195
196 match span {
197 Some(span) => {
198 cx.emit_span_lint(
199 USAGE_OF_TY_TYKIND,
200 path.span,
201 TykindKind { suggestion: span },
202 );
203 }
204 None => cx.emit_span_lint(USAGE_OF_TY_TYKIND, path.span, TykindDiag),
205 }
206 } else if !ty.span.from_expansion()
207 && path.segments.len() > 1
208 && let Some(ty) = is_ty_or_ty_ctxt(cx, path)
209 {
210 cx.emit_span_lint(
211 USAGE_OF_QUALIFIED_TY,
212 path.span,
213 TyQualified { ty, suggestion: path.span },
214 );
215 }
216 }
217 _ => {}
218 }
219 }
220}
221
222fn lint_ty_kind_usage(cx: &LateContext<'_>, res: &Res) -> bool {
223 if let Some(did) = res.opt_def_id() {
224 cx.tcx.is_diagnostic_item(sym::TyKind, did) || cx.tcx.is_diagnostic_item(sym::IrTyKind, did)
225 } else {
226 false
227 }
228}
229
230fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &hir::Path<'_>) -> Option<String> {
231 match &path.res {
232 Res::Def(_, def_id) => {
233 if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(*def_id) {
234 return Some(format!("{}{}", name, gen_args(path.segments.last().unwrap())));
235 }
236 }
237 Res::SelfTyAlias { alias_to: did, is_trait_impl: false, .. } => {
239 if let ty::Adt(adt, args) = cx.tcx.type_of(did).instantiate_identity().kind()
240 && let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(adt.did())
241 {
242 return Some(format!("{}<{}>", name, args[0]));
243 }
244 }
245 _ => (),
246 }
247
248 None
249}
250
251fn gen_args(segment: &hir::PathSegment<'_>) -> String {
252 if let Some(args) = &segment.args {
253 let lifetimes = args
254 .args
255 .iter()
256 .filter_map(|arg| {
257 if let hir::GenericArg::Lifetime(lt) = arg {
258 Some(lt.ident.to_string())
259 } else {
260 None
261 }
262 })
263 .collect::<Vec<_>>();
264
265 if !lifetimes.is_empty() {
266 return format!("<{}>", lifetimes.join(", "));
267 }
268 }
269
270 String::new()
271}
272
273declare_tool_lint! {
274 pub rustc::NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT,
277 Allow,
278 "non-glob import of `rustc_type_ir::inherent`",
279 report_in_external_macro: true
280}
281
282declare_tool_lint! {
283 pub rustc::USAGE_OF_TYPE_IR_INHERENT,
287 Allow,
288 "usage `rustc_type_ir::inherent` outside of trait system",
289 report_in_external_macro: true
290}
291
292declare_tool_lint! {
293 pub rustc::USAGE_OF_TYPE_IR_TRAITS,
300 Allow,
301 "usage `rustc_type_ir`-specific abstraction traits outside of trait system",
302 report_in_external_macro: true
303}
304declare_tool_lint! {
305 pub rustc::DIRECT_USE_OF_RUSTC_TYPE_IR,
310 Allow,
311 "usage `rustc_type_ir` abstraction outside of trait system",
312 report_in_external_macro: true
313}
314
315declare_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]);
316
317impl<'tcx> LateLintPass<'tcx> for TypeIr {
318 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
319 let res_def_id = match expr.kind {
320 hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => path.res.opt_def_id(),
321 hir::ExprKind::Path(hir::QPath::TypeRelative(..)) | hir::ExprKind::MethodCall(..) => {
322 cx.typeck_results().type_dependent_def_id(expr.hir_id)
323 }
324 _ => return,
325 };
326 let Some(res_def_id) = res_def_id else {
327 return;
328 };
329 if let Some(assoc_item) = cx.tcx.opt_associated_item(res_def_id)
330 && let Some(trait_def_id) = assoc_item.trait_container(cx.tcx)
331 && (cx.tcx.is_diagnostic_item(sym::type_ir_interner, trait_def_id)
332 | cx.tcx.is_diagnostic_item(sym::type_ir_infer_ctxt_like, trait_def_id))
333 {
334 cx.emit_span_lint(USAGE_OF_TYPE_IR_TRAITS, expr.span, TypeIrTraitUsage);
335 }
336 }
337
338 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
339 let rustc_hir::ItemKind::Use(path, kind) = item.kind else { return };
340
341 let is_mod_inherent = |res: Res| {
342 res.opt_def_id()
343 .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id))
344 };
345
346 if let Some(seg) = path.segments.iter().find(|seg| is_mod_inherent(seg.res)) {
348 cx.emit_span_lint(USAGE_OF_TYPE_IR_INHERENT, seg.ident.span, TypeIrInherentUsage);
349 }
350 else if let Some(type_ns) = path.res.type_ns
352 && is_mod_inherent(type_ns)
353 {
354 cx.emit_span_lint(
355 USAGE_OF_TYPE_IR_INHERENT,
356 path.segments.last().unwrap().ident.span,
357 TypeIrInherentUsage,
358 );
359 }
360
361 let (lo, hi, snippet) = match path.segments {
362 [.., penultimate, segment] if is_mod_inherent(penultimate.res) => {
363 (segment.ident.span, item.kind.ident().unwrap().span, "*")
364 }
365 [.., segment]
366 if let Some(type_ns) = path.res.type_ns
367 && is_mod_inherent(type_ns)
368 && let rustc_hir::UseKind::Single(ident) = kind =>
369 {
370 let (lo, snippet) =
371 match cx.tcx.sess.source_map().span_to_snippet(path.span).as_deref() {
372 Ok("self") => (path.span, "*"),
373 _ => (segment.ident.span.shrink_to_hi(), "::*"),
374 };
375 (lo, if segment.ident == ident { lo } else { ident.span }, snippet)
376 }
377 _ => return,
378 };
379 cx.emit_span_lint(
380 NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT,
381 path.span,
382 NonGlobImportTypeIrInherent { suggestion: lo.eq_ctxt(hi).then(|| lo.to(hi)), snippet },
383 );
384 }
385
386 fn check_path(
387 &mut self,
388 cx: &LateContext<'tcx>,
389 path: &rustc_hir::Path<'tcx>,
390 _: rustc_hir::HirId,
391 ) {
392 if let Some(seg) = path.segments.iter().find(|seg| {
393 seg.res
394 .opt_def_id()
395 .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::type_ir, def_id))
396 }) {
397 cx.emit_span_lint(DIRECT_USE_OF_RUSTC_TYPE_IR, seg.ident.span, TypeIrDirectUse);
398 }
399 }
400}
401
402declare_tool_lint! {
403 pub rustc::LINT_PASS_IMPL_WITHOUT_MACRO,
406 Allow,
407 "`impl LintPass` without the `declare_lint_pass!` or `impl_lint_pass!` macros"
408}
409
410declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]);
411
412impl EarlyLintPass for LintPassImpl {
413 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
414 if let ast::ItemKind::Impl(box ast::Impl { of_trait: Some(lint_pass), .. }) = &item.kind {
415 if let Some(last) = lint_pass.path.segments.last() {
416 if last.ident.name == sym::LintPass {
417 let expn_data = lint_pass.path.span.ctxt().outer_expn_data();
418 let call_site = expn_data.call_site;
419 if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass)
420 && call_site.ctxt().outer_expn_data().kind
421 != ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass)
422 {
423 cx.emit_span_lint(
424 LINT_PASS_IMPL_WITHOUT_MACRO,
425 lint_pass.path.span,
426 LintPassByHand,
427 );
428 }
429 }
430 }
431 }
432 }
433}
434
435declare_tool_lint! {
436 pub rustc::UNTRANSLATABLE_DIAGNOSTIC,
442 Allow,
443 "prevent creation of diagnostics which cannot be translated",
444 report_in_external_macro: true,
445 @eval_always = true
446}
447
448declare_tool_lint! {
449 pub rustc::DIAGNOSTIC_OUTSIDE_OF_IMPL,
456 Allow,
457 "prevent diagnostic creation outside of `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls",
458 report_in_external_macro: true,
459 @eval_always = true
460}
461
462declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]);
463
464impl LateLintPass<'_> for Diagnostics {
465 fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
466 let collect_args_tys_and_spans = |args: &[hir::Expr<'_>], reserve_one_extra: bool| {
467 let mut result = Vec::with_capacity(args.len() + usize::from(reserve_one_extra));
468 result.extend(args.iter().map(|arg| (cx.typeck_results().expr_ty(arg), arg.span)));
469 result
470 };
471 let (span, def_id, fn_gen_args, arg_tys_and_spans) = match expr.kind {
473 hir::ExprKind::Call(callee, args) => {
474 match cx.typeck_results().node_type(callee.hir_id).kind() {
475 &ty::FnDef(def_id, fn_gen_args) => {
476 (callee.span, def_id, fn_gen_args, collect_args_tys_and_spans(args, false))
477 }
478 _ => return, }
480 }
481 hir::ExprKind::MethodCall(_segment, _recv, args, _span) => {
482 let Some((span, def_id, fn_gen_args)) = typeck_results_of_method_fn(cx, expr)
483 else {
484 return;
485 };
486 let mut args = collect_args_tys_and_spans(args, true);
487 args.insert(0, (cx.tcx.types.self_param, _recv.span)); (span, def_id, fn_gen_args, args)
489 }
490 _ => return,
491 };
492
493 Self::diagnostic_outside_of_impl(cx, span, expr.hir_id, def_id, fn_gen_args);
494 Self::untranslatable_diagnostic(cx, def_id, &arg_tys_and_spans);
495 }
496}
497
498impl Diagnostics {
499 fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: MiddleTy<'cx>) -> bool {
501 if let Some(adt_def) = ty.ty_adt_def()
502 && let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did())
503 && matches!(name, sym::DiagMessage | sym::SubdiagMessage)
504 {
505 true
506 } else {
507 false
508 }
509 }
510
511 fn untranslatable_diagnostic<'cx>(
512 cx: &LateContext<'cx>,
513 def_id: DefId,
514 arg_tys_and_spans: &[(MiddleTy<'cx>, Span)],
515 ) {
516 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
517 let predicates = cx.tcx.predicates_of(def_id).instantiate_identity(cx.tcx).predicates;
518 for (i, ¶m_ty) in fn_sig.inputs().iter().enumerate() {
519 if let ty::Param(sig_param) = param_ty.kind() {
520 for pred in predicates.iter() {
522 if let Some(trait_pred) = pred.as_trait_clause()
523 && let trait_ref = trait_pred.skip_binder().trait_ref
524 && trait_ref.self_ty() == param_ty && cx.tcx.is_diagnostic_item(sym::Into, trait_ref.def_id)
526 && let ty1 = trait_ref.args.type_at(1)
527 && Self::is_diag_message(cx, ty1)
528 {
529 let (arg_ty, arg_span) = arg_tys_and_spans[i];
533
534 let is_translatable = Self::is_diag_message(cx, arg_ty)
536 || matches!(arg_ty.kind(), ty::Param(arg_param) if arg_param.name == sig_param.name);
537 if !is_translatable {
538 cx.emit_span_lint(
539 UNTRANSLATABLE_DIAGNOSTIC,
540 arg_span,
541 UntranslatableDiag,
542 );
543 }
544 }
545 }
546 }
547 }
548 }
549
550 fn diagnostic_outside_of_impl<'cx>(
551 cx: &LateContext<'cx>,
552 span: Span,
553 current_id: HirId,
554 def_id: DefId,
555 fn_gen_args: GenericArgsRef<'cx>,
556 ) {
557 let Some(inst) =
559 ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, fn_gen_args).ok().flatten()
560 else {
561 return;
562 };
563 let has_attr = cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics);
564 if !has_attr {
565 return;
566 };
567
568 for (hir_id, _parent) in cx.tcx.hir_parent_iter(current_id) {
569 if let Some(owner_did) = hir_id.as_owner()
570 && cx.tcx.has_attr(owner_did, sym::rustc_lint_diagnostics)
571 {
572 return;
574 }
575 }
576
577 let mut is_inside_appropriate_impl = false;
583 for (_hir_id, parent) in cx.tcx.hir_parent_iter(current_id) {
584 debug!(?parent);
585 if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) = parent
586 && let hir::Impl { of_trait: Some(of_trait), .. } = impl_
587 && let Some(def_id) = of_trait.trait_def_id()
588 && let Some(name) = cx.tcx.get_diagnostic_name(def_id)
589 && matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic)
590 {
591 is_inside_appropriate_impl = true;
592 break;
593 }
594 }
595 debug!(?is_inside_appropriate_impl);
596 if !is_inside_appropriate_impl {
597 cx.emit_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl);
598 }
599 }
600}
601
602declare_tool_lint! {
603 pub rustc::BAD_OPT_ACCESS,
606 Deny,
607 "prevent using options by field access when there is a wrapper function",
608 report_in_external_macro: true
609}
610
611declare_lint_pass!(BadOptAccess => [BAD_OPT_ACCESS]);
612
613impl LateLintPass<'_> for BadOptAccess {
614 fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
615 let hir::ExprKind::Field(base, target) = expr.kind else { return };
616 let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return };
617 if !cx.tcx.has_attr(adt_def.did(), sym::rustc_lint_opt_ty) {
620 return;
621 }
622
623 for field in adt_def.all_fields() {
624 if field.name == target.name
625 && let Some(attr) =
626 cx.tcx.get_attr(field.did, sym::rustc_lint_opt_deny_field_access)
627 && let Some(items) = attr.meta_item_list()
628 && let Some(item) = items.first()
629 && let Some(lit) = item.lit()
630 && let ast::LitKind::Str(val, _) = lit.kind
631 {
632 cx.emit_span_lint(
633 BAD_OPT_ACCESS,
634 expr.span,
635 BadOptAccessDiag { msg: val.as_str() },
636 );
637 }
638 }
639 }
640}
641
642declare_tool_lint! {
643 pub rustc::SPAN_USE_EQ_CTXT,
644 Allow,
645 "forbid uses of `==` with `Span::ctxt`, suggest `Span::eq_ctxt` instead",
646 report_in_external_macro: true
647}
648
649declare_lint_pass!(SpanUseEqCtxt => [SPAN_USE_EQ_CTXT]);
650
651impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt {
652 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
653 if let hir::ExprKind::Binary(
654 hir::BinOp { node: hir::BinOpKind::Eq | hir::BinOpKind::Ne, .. },
655 lhs,
656 rhs,
657 ) = expr.kind
658 {
659 if is_span_ctxt_call(cx, lhs) && is_span_ctxt_call(cx, rhs) {
660 cx.emit_span_lint(SPAN_USE_EQ_CTXT, expr.span, SpanUseEqCtxtDiag);
661 }
662 }
663 }
664}
665
666fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
667 match &expr.kind {
668 hir::ExprKind::MethodCall(..) => cx
669 .typeck_results()
670 .type_dependent_def_id(expr.hir_id)
671 .is_some_and(|call_did| cx.tcx.is_diagnostic_item(sym::SpanCtxt, call_did)),
672
673 _ => false,
674 }
675}
676
677declare_tool_lint! {
678 pub rustc::SYMBOL_INTERN_STRING_LITERAL,
680 Allow,
683 "Forbid uses of string literals in `Symbol::intern`, suggesting preinterning instead",
684 report_in_external_macro: true
685}
686
687declare_lint_pass!(SymbolInternStringLiteral => [SYMBOL_INTERN_STRING_LITERAL]);
688
689impl<'tcx> LateLintPass<'tcx> for SymbolInternStringLiteral {
690 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
691 if let hir::ExprKind::Call(path, [arg]) = expr.kind
692 && let hir::ExprKind::Path(ref qpath) = path.kind
693 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
694 && cx.tcx.is_diagnostic_item(sym::SymbolIntern, def_id)
695 && let hir::ExprKind::Lit(kind) = arg.kind
696 && let rustc_ast::LitKind::Str(_, _) = kind.node
697 {
698 cx.emit_span_lint(
699 SYMBOL_INTERN_STRING_LITERAL,
700 kind.span,
701 SymbolInternStringLiteralDiag,
702 );
703 }
704 }
705}