]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_lint/src/internal.rs
New upstream version 1.60.0+dfsg1
[rustc.git] / compiler / rustc_lint / src / internal.rs
CommitLineData
532ac7d7
XL
1//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
2//! Clippy.
3
dfeec247 4use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
136023e0 5use rustc_ast as ast;
dfeec247 6use rustc_errors::Applicability;
1b1a35ee 7use rustc_hir::def::Res;
5099ac24
FG
8use rustc_hir::{Expr, ExprKind, GenericArg, Path, PathSegment, QPath};
9use rustc_hir::{HirId, Item, ItemKind, Node, Ty, TyKind};
1b1a35ee 10use rustc_middle::ty;
136023e0 11use rustc_session::{declare_lint_pass, declare_tool_lint};
dfeec247 12use rustc_span::hygiene::{ExpnKind, MacroKind};
136023e0 13use rustc_span::symbol::{kw, sym, Symbol};
532ac7d7 14
416331ca
XL
15declare_tool_lint! {
16 pub rustc::DEFAULT_HASH_TYPES,
532ac7d7 17 Allow,
dfeec247
XL
18 "forbid HashMap and HashSet and suggest the FxHash* variants",
19 report_in_external_macro: true
532ac7d7
XL
20}
21
136023e0
XL
22declare_lint_pass!(DefaultHashTypes => [DEFAULT_HASH_TYPES]);
23
24impl LateLintPass<'_> for DefaultHashTypes {
25 fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) {
26 let def_id = match path.res {
27 Res::Def(rustc_hir::def::DefKind::Struct, id) => id,
28 _ => return,
29 };
30 if matches!(cx.tcx.hir().get(hir_id), Node::Item(Item { kind: ItemKind::Use(..), .. })) {
31 // don't lint imports, only actual usages
32 return;
532ac7d7 33 }
c295e0f8
XL
34 let replace = match cx.tcx.get_diagnostic_name(def_id) {
35 Some(sym::HashMap) => "FxHashMap",
36 Some(sym::HashSet) => "FxHashSet",
37 _ => return,
136023e0
XL
38 };
39 cx.struct_span_lint(DEFAULT_HASH_TYPES, path.span, |lint| {
40 let msg = format!(
41 "prefer `{}` over `{}`, it has better performance",
42 replace,
43 cx.tcx.item_name(def_id)
44 );
45 lint.build(&msg)
46 .note(&format!("a `use rustc_data_structures::fx::{}` may be necessary", replace))
47 .emit();
48 });
532ac7d7
XL
49 }
50}
51
416331ca 52declare_tool_lint! {
5099ac24 53 pub rustc::POTENTIAL_QUERY_INSTABILITY,
532ac7d7 54 Allow,
5099ac24 55 "require explicit opt-in when using potentially unstable methods or functions",
dfeec247 56 report_in_external_macro: true
532ac7d7
XL
57}
58
5099ac24
FG
59declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY]);
60
61impl LateLintPass<'_> for QueryStability {
62 fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
63 // FIXME(rustdoc): This lint uses typecheck results, causing rustdoc to
64 // error if there are resolution failures.
65 //
66 // As internal lints are currently always run if there are `unstable_options`,
67 // they are added to the lint store of rustdoc. Internal lints are also
68 // not used via the `lint_mod` query. Crate lints run outside of a query
69 // so rustdoc currently doesn't disable them.
70 //
71 // Instead of relying on this, either change crate lints to a query disabled by
72 // rustdoc, only run internal lints if the user is explicitly opting in
73 // or figure out a different way to avoid running lints for rustdoc.
74 if cx.tcx.sess.opts.actually_rustdoc {
75 return;
76 }
77
78 let (span, def_id, substs) = match expr.kind {
79 ExprKind::MethodCall(segment, _, _)
80 if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
81 {
82 (segment.ident.span, def_id, cx.typeck_results().node_substs(expr.hir_id))
83 },
84 _ => {
85 let &ty::FnDef(def_id, substs) =
86 cx.typeck_results()
87 .node_type(expr.hir_id)
88 .kind() else { return };
89 (expr.span, def_id, substs)
90 }
91 };
92 if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) {
93 let def_id = instance.def_id();
94 if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
95 cx.struct_span_lint(POTENTIAL_QUERY_INSTABILITY, span, |lint| {
96 let msg = format!(
97 "using `{}` can result in unstable query results",
98 cx.tcx.item_name(def_id)
99 );
100 lint.build(&msg)
101 .note("if you believe this case to be fine, allow this lint and add a comment explaining your rationale")
102 .emit();
103 })
104 }
105 }
106 }
107}
108
416331ca 109declare_tool_lint! {
5099ac24 110 pub rustc::USAGE_OF_TY_TYKIND,
48663c56 111 Allow,
5099ac24 112 "usage of `ty::TyKind` outside of the `ty::sty` module",
dfeec247 113 report_in_external_macro: true
48663c56
XL
114}
115
416331ca
XL
116declare_tool_lint! {
117 pub rustc::USAGE_OF_QUALIFIED_TY,
48663c56 118 Allow,
dfeec247
XL
119 "using `ty::{Ty,TyCtxt}` instead of importing it",
120 report_in_external_macro: true
48663c56
XL
121}
122
123declare_lint_pass!(TyTyKind => [
124 USAGE_OF_TY_TYKIND,
48663c56
XL
125 USAGE_OF_QUALIFIED_TY,
126]);
532ac7d7 127
f035d41b
XL
128impl<'tcx> LateLintPass<'tcx> for TyTyKind {
129 fn check_path(&mut self, cx: &LateContext<'_>, path: &'tcx Path<'tcx>, _: HirId) {
532ac7d7
XL
130 let segments = path.segments.iter().rev().skip(1).rev();
131
132 if let Some(last) = segments.last() {
133 let span = path.span.with_hi(last.ident.span.hi());
134 if lint_ty_kind_usage(cx, last) {
74b04a01
XL
135 cx.struct_span_lint(USAGE_OF_TY_TYKIND, span, |lint| {
136 lint.build("usage of `ty::TyKind::<kind>`")
137 .span_suggestion(
138 span,
139 "try using ty::<kind> directly",
140 "ty".to_string(),
141 Applicability::MaybeIncorrect, // ty maybe needs an import
142 )
143 .emit();
144 })
532ac7d7
XL
145 }
146 }
147 }
148
f035d41b 149 fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx>) {
e74abb32 150 match &ty.kind {
3c0e092e
XL
151 TyKind::Path(QPath::Resolved(_, path)) => {
152 if let Some(last) = path.segments.iter().last() {
153 if lint_ty_kind_usage(cx, last) {
154 cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, |lint| {
155 lint.build("usage of `ty::TyKind`")
156 .help("try using `Ty` instead")
157 .emit();
158 })
159 } else {
160 if ty.span.from_expansion() {
161 return;
162 }
163 if let Some(t) = is_ty_or_ty_ctxt(cx, ty) {
164 if path.segments.len() > 1 {
165 cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, |lint| {
166 lint.build(&format!("usage of qualified `ty::{}`", t))
167 .span_suggestion(
168 path.span,
5099ac24 169 "try importing it and using it unqualified",
3c0e092e
XL
170 t,
171 // The import probably needs to be changed
172 Applicability::MaybeIncorrect,
173 )
174 .emit();
175 })
48663c56
XL
176 }
177 }
178 }
179 }
180 }
48663c56 181 _ => {}
532ac7d7
XL
182 }
183 }
184}
185
f035d41b 186fn lint_ty_kind_usage(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> bool {
e1599b0c
XL
187 if let Some(res) = segment.res {
188 if let Some(did) = res.opt_def_id() {
189 return cx.tcx.is_diagnostic_item(sym::TyKind, did);
532ac7d7
XL
190 }
191 }
192
193 false
194}
48663c56 195
f035d41b 196fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, ty: &Ty<'_>) -> Option<String> {
3c0e092e
XL
197 if let TyKind::Path(QPath::Resolved(_, path)) = &ty.kind {
198 match path.res {
199 Res::Def(_, def_id) => {
200 if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(def_id) {
201 return Some(format!("{}{}", name, gen_args(path.segments.last().unwrap())));
1b1a35ee 202 }
3c0e092e
XL
203 }
204 // Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait.
5099ac24 205 Res::SelfTy { trait_: None, alias_to: Some((did, _)) } => {
3c0e092e
XL
206 if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() {
207 if let Some(name @ (sym::Ty | sym::TyCtxt)) =
208 cx.tcx.get_diagnostic_name(adt.did)
209 {
210 // NOTE: This path is currently unreachable as `Ty<'tcx>` is
211 // defined as a type alias meaning that `impl<'tcx> Ty<'tcx>`
212 // is not actually allowed.
213 //
214 // I(@lcnr) still kept this branch in so we don't miss this
215 // if we ever change it in the future.
216 return Some(format!("{}<{}>", name, substs[0]));
1b1a35ee
XL
217 }
218 }
48663c56 219 }
3c0e092e 220 _ => (),
48663c56 221 }
48663c56
XL
222 }
223
224 None
225}
226
dfeec247 227fn gen_args(segment: &PathSegment<'_>) -> String {
48663c56
XL
228 if let Some(args) = &segment.args {
229 let lifetimes = args
230 .args
231 .iter()
232 .filter_map(|arg| {
233 if let GenericArg::Lifetime(lt) = arg {
234 Some(lt.name.ident().to_string())
235 } else {
236 None
237 }
238 })
239 .collect::<Vec<_>>();
240
241 if !lifetimes.is_empty() {
242 return format!("<{}>", lifetimes.join(", "));
243 }
244 }
245
246 String::new()
247}
416331ca
XL
248
249declare_tool_lint! {
250 pub rustc::LINT_PASS_IMPL_WITHOUT_MACRO,
251 Allow,
252 "`impl LintPass` without the `declare_lint_pass!` or `impl_lint_pass!` macros"
253}
254
255declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]);
256
257impl EarlyLintPass for LintPassImpl {
136023e0 258 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
3c0e092e 259 if let ast::ItemKind::Impl(box ast::Impl { of_trait: Some(lint_pass), .. }) = &item.kind {
416331ca
XL
260 if let Some(last) = lint_pass.path.segments.last() {
261 if last.ident.name == sym::LintPass {
e1599b0c
XL
262 let expn_data = lint_pass.path.span.ctxt().outer_expn_data();
263 let call_site = expn_data.call_site;
136023e0
XL
264 if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass)
265 && call_site.ctxt().outer_expn_data().kind
266 != ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass)
267 {
e1599b0c
XL
268 cx.struct_span_lint(
269 LINT_PASS_IMPL_WITHOUT_MACRO,
270 lint_pass.path.span,
74b04a01
XL
271 |lint| {
272 lint.build("implementing `LintPass` by hand")
273 .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
274 .emit();
275 },
e1599b0c 276 )
416331ca
XL
277 }
278 }
279 }
280 }
281 }
282}
fc512014
XL
283
284declare_tool_lint! {
285 pub rustc::EXISTING_DOC_KEYWORD,
286 Allow,
287 "Check that documented keywords in std and core actually exist",
288 report_in_external_macro: true
289}
290
291declare_lint_pass!(ExistingDocKeyword => [EXISTING_DOC_KEYWORD]);
292
293fn is_doc_keyword(s: Symbol) -> bool {
294 s <= kw::Union
295}
296
297impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword {
298 fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
6a06907d 299 for attr in cx.tcx.hir().attrs(item.hir_id()) {
fc512014
XL
300 if !attr.has_name(sym::doc) {
301 continue;
302 }
303 if let Some(list) = attr.meta_item_list() {
304 for nested in list {
305 if nested.has_name(sym::keyword) {
306 let v = nested
307 .value_str()
308 .expect("#[doc(keyword = \"...\")] expected a value!");
309 if is_doc_keyword(v) {
310 return;
311 }
312 cx.struct_span_lint(EXISTING_DOC_KEYWORD, attr.span, |lint| {
313 lint.build(&format!(
314 "Found non-existing keyword `{}` used in \
315 `#[doc(keyword = \"...\")]`",
316 v,
317 ))
318 .help("only existing keywords are allowed in core/std")
319 .emit();
320 });
321 }
322 }
323 }
324 }
325 }
326}