]>
Commit | Line | Data |
---|---|---|
c34b1796 AL |
1 | //! Lints in the Rust compiler. |
2 | //! | |
3 | //! This contains lints which can feasibly be implemented as their own | |
ba9703b0 XL |
4 | //! AST visitor. Also see `rustc_session::lint::builtin`, which contains the |
5 | //! definitions of lints that are emitted directly inside the main compiler. | |
c34b1796 AL |
6 | //! |
7 | //! To add a new lint to rustc, declare it here using `declare_lint!()`. | |
8 | //! Then add code to emit the new lint in the appropriate circumstances. | |
9 | //! You can do that in an existing `LintPass` if it makes sense, or in a | |
10 | //! new `LintPass`, or using `Session::add_lint` elsewhere in the | |
11 | //! compiler. Only do the latter if the check can't be written cleanly as a | |
12 | //! `LintPass` (also, note that such lints will need to be defined in | |
ba9703b0 | 13 | //! `rustc_session::lint::builtin`, not here). |
c34b1796 | 14 | //! |
9fa01778 XL |
15 | //! If you define a new `EarlyLintPass`, you will also need to add it to the |
16 | //! `add_early_builtin!` or `add_early_builtin_with_new!` invocation in | |
17 | //! `lib.rs`. Use the former for unit-like structs and the latter for structs | |
18 | //! with a `pub fn new()`. | |
19 | //! | |
20 | //! If you define a new `LateLintPass`, you will also need to add it to the | |
21 | //! `late_lint_methods!` invocation in `lib.rs`. | |
c34b1796 | 22 | |
3dfed10e | 23 | use crate::{ |
1b1a35ee XL |
24 | types::{transparent_newtype_field, CItemKind}, |
25 | EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext, | |
3dfed10e | 26 | }; |
6a06907d | 27 | use rustc_ast::attr; |
74b04a01 XL |
28 | use rustc_ast::tokenstream::{TokenStream, TokenTree}; |
29 | use rustc_ast::visit::{FnCtxt, FnKind}; | |
3dfed10e | 30 | use rustc_ast::{self as ast, *}; |
74b04a01 | 31 | use rustc_ast_pretty::pprust::{self, expr_to_string}; |
f035d41b | 32 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
3dfed10e | 33 | use rustc_data_structures::stack::ensure_sufficient_stack; |
064997fb FG |
34 | use rustc_errors::{ |
35 | fluent, Applicability, Diagnostic, DiagnosticMessage, DiagnosticStyledString, | |
36 | LintDiagnosticBuilder, MultiSpan, | |
37 | }; | |
3c0e092e | 38 | use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability}; |
dfeec247 XL |
39 | use rustc_hir as hir; |
40 | use rustc_hir::def::{DefKind, Res}; | |
94222f64 | 41 | use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID}; |
04454e1e | 42 | use rustc_hir::{ForeignItemKind, GenericParamKind, HirId, PatKind, PredicateOrigin}; |
1b1a35ee | 43 | use rustc_index::vec::Idx; |
064997fb | 44 | use rustc_middle::lint::in_external_macro; |
c295e0f8 | 45 | use rustc_middle::ty::layout::{LayoutError, LayoutOf}; |
1b1a35ee | 46 | use rustc_middle::ty::print::with_no_trimmed_paths; |
04454e1e | 47 | use rustc_middle::ty::subst::GenericArgKind; |
5869c6ff | 48 | use rustc_middle::ty::Instance; |
c295e0f8 | 49 | use rustc_middle::ty::{self, Ty, TyCtxt}; |
94222f64 | 50 | use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason}; |
dfeec247 XL |
51 | use rustc_span::edition::Edition; |
52 | use rustc_span::source_map::Spanned; | |
f9f354fc | 53 | use rustc_span::symbol::{kw, sym, Ident, Symbol}; |
04454e1e | 54 | use rustc_span::{BytePos, InnerSpan, Span}; |
c295e0f8 | 55 | use rustc_target::abi::VariantIdx; |
5e7ed085 | 56 | use rustc_trait_selection::traits::{self, misc::can_type_implement_copy}; |
e9174d1e | 57 | |
dfeec247 | 58 | use crate::nonstandard_style::{method_context, MethodLateContext}; |
9fa01778 | 59 | |
dfeec247 | 60 | use std::fmt::Write; |
3dfed10e | 61 | use tracing::{debug, trace}; |
c34b1796 | 62 | |
ba9703b0 | 63 | // hardwired lints from librustc_middle |
dfeec247 | 64 | pub use rustc_session::lint::builtin::*; |
c34b1796 | 65 | |
b039eaaf | 66 | declare_lint! { |
1b1a35ee XL |
67 | /// The `while_true` lint detects `while true { }`. |
68 | /// | |
69 | /// ### Example | |
70 | /// | |
71 | /// ```rust,no_run | |
72 | /// while true { | |
73 | /// | |
74 | /// } | |
75 | /// ``` | |
76 | /// | |
77 | /// {{produces}} | |
78 | /// | |
79 | /// ### Explanation | |
80 | /// | |
81 | /// `while true` should be replaced with `loop`. A `loop` expression is | |
82 | /// the preferred way to write an infinite loop because it more directly | |
83 | /// expresses the intent of the loop. | |
b039eaaf SL |
84 | WHILE_TRUE, |
85 | Warn, | |
86 | "suggest using `loop { }` instead of `while true { }`" | |
87 | } | |
c34b1796 | 88 | |
532ac7d7 | 89 | declare_lint_pass!(WhileTrue => [WHILE_TRUE]); |
c34b1796 | 90 | |
416331ca XL |
91 | /// Traverse through any amount of parenthesis and return the first non-parens expression. |
92 | fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr { | |
e74abb32 | 93 | while let ast::ExprKind::Paren(sub) = &expr.kind { |
416331ca XL |
94 | expr = sub; |
95 | } | |
96 | expr | |
97 | } | |
98 | ||
99 | impl EarlyLintPass for WhileTrue { | |
100 | fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { | |
5869c6ff | 101 | if let ast::ExprKind::While(cond, _, label) = &e.kind { |
e74abb32 XL |
102 | if let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind { |
103 | if let ast::LitKind::Bool(true) = lit.kind { | |
e1599b0c | 104 | if !lit.span.from_expansion() { |
5869c6ff | 105 | let condition_span = e.span.with_hi(cond.span.hi()); |
74b04a01 | 106 | cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| { |
064997fb | 107 | lint.build(fluent::lint::builtin_while_true) |
74b04a01 XL |
108 | .span_suggestion_short( |
109 | condition_span, | |
064997fb | 110 | fluent::lint::suggestion, |
5869c6ff XL |
111 | format!( |
112 | "{}loop", | |
113 | label.map_or_else(String::new, |label| format!( | |
114 | "{}: ", | |
115 | label.ident, | |
116 | )) | |
117 | ), | |
74b04a01 XL |
118 | Applicability::MachineApplicable, |
119 | ) | |
120 | .emit(); | |
121 | }) | |
ea8adc8c | 122 | } |
b039eaaf | 123 | } |
c34b1796 AL |
124 | } |
125 | } | |
126 | } | |
127 | } | |
128 | ||
129 | declare_lint! { | |
1b1a35ee XL |
130 | /// The `box_pointers` lints use of the Box type. |
131 | /// | |
132 | /// ### Example | |
133 | /// | |
134 | /// ```rust,compile_fail | |
135 | /// #![deny(box_pointers)] | |
136 | /// struct Foo { | |
137 | /// x: Box<isize>, | |
138 | /// } | |
139 | /// ``` | |
140 | /// | |
141 | /// {{produces}} | |
142 | /// | |
143 | /// ### Explanation | |
144 | /// | |
145 | /// This lint is mostly historical, and not particularly useful. `Box<T>` | |
146 | /// used to be built into the language, and the only way to do heap | |
147 | /// allocation. Today's Rust can call into other allocators, etc. | |
b039eaaf SL |
148 | BOX_POINTERS, |
149 | Allow, | |
150 | "use of owned (Box type) heap memory" | |
c34b1796 AL |
151 | } |
152 | ||
532ac7d7 | 153 | declare_lint_pass!(BoxPointers => [BOX_POINTERS]); |
b039eaaf SL |
154 | |
155 | impl BoxPointers { | |
5099ac24 FG |
156 | fn check_heap_type(&self, cx: &LateContext<'_>, span: Span, ty: Ty<'_>) { |
157 | for leaf in ty.walk() { | |
ba9703b0 XL |
158 | if let GenericArgKind::Type(leaf_ty) = leaf.unpack() { |
159 | if leaf_ty.is_box() { | |
160 | cx.struct_span_lint(BOX_POINTERS, span, |lint| { | |
064997fb | 161 | lint.build(fluent::lint::builtin_box_pointers).set_arg("ty", ty).emit(); |
ba9703b0 XL |
162 | }); |
163 | } | |
c34b1796 AL |
164 | } |
165 | } | |
166 | } | |
167 | } | |
168 | ||
f035d41b XL |
169 | impl<'tcx> LateLintPass<'tcx> for BoxPointers { |
170 | fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { | |
e74abb32 | 171 | match it.kind { |
dfeec247 XL |
172 | hir::ItemKind::Fn(..) |
173 | | hir::ItemKind::TyAlias(..) | |
174 | | hir::ItemKind::Enum(..) | |
175 | | hir::ItemKind::Struct(..) | |
176 | | hir::ItemKind::Union(..) => { | |
6a06907d | 177 | self.check_heap_type(cx, it.span, cx.tcx.type_of(it.def_id)) |
c30ab7b3 | 178 | } |
dfeec247 | 179 | _ => (), |
d9579d0f | 180 | } |
d9579d0f | 181 | |
b039eaaf | 182 | // If it's a struct, we also have to check the fields' types |
e74abb32 | 183 | match it.kind { |
dfeec247 | 184 | hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { |
b039eaaf | 185 | for struct_field in struct_def.fields() { |
416331ca | 186 | let def_id = cx.tcx.hir().local_def_id(struct_field.hir_id); |
dfeec247 | 187 | self.check_heap_type(cx, struct_field.span, cx.tcx.type_of(def_id)); |
b039eaaf | 188 | } |
d9579d0f | 189 | } |
c30ab7b3 | 190 | _ => (), |
d9579d0f AL |
191 | } |
192 | } | |
193 | ||
f035d41b | 194 | fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) { |
3dfed10e | 195 | let ty = cx.typeck_results().node_type(e.hir_id); |
b039eaaf | 196 | self.check_heap_type(cx, e.span, ty); |
c34b1796 AL |
197 | } |
198 | } | |
199 | ||
c34b1796 | 200 | declare_lint! { |
1b1a35ee XL |
201 | /// The `non_shorthand_field_patterns` lint detects using `Struct { x: x }` |
202 | /// instead of `Struct { x }` in a pattern. | |
203 | /// | |
204 | /// ### Example | |
205 | /// | |
206 | /// ```rust | |
207 | /// struct Point { | |
208 | /// x: i32, | |
209 | /// y: i32, | |
210 | /// } | |
211 | /// | |
212 | /// | |
213 | /// fn main() { | |
214 | /// let p = Point { | |
215 | /// x: 5, | |
216 | /// y: 5, | |
217 | /// }; | |
218 | /// | |
219 | /// match p { | |
220 | /// Point { x: x, y: y } => (), | |
221 | /// } | |
222 | /// } | |
223 | /// ``` | |
224 | /// | |
225 | /// {{produces}} | |
226 | /// | |
227 | /// ### Explanation | |
228 | /// | |
229 | /// The preferred style is to avoid the repetition of specifying both the | |
230 | /// field name and the binding name if both identifiers are the same. | |
c34b1796 AL |
231 | NON_SHORTHAND_FIELD_PATTERNS, |
232 | Warn, | |
abe05a73 | 233 | "using `Struct { x: x }` instead of `Struct { x }` in a pattern" |
c34b1796 AL |
234 | } |
235 | ||
532ac7d7 | 236 | declare_lint_pass!(NonShorthandFieldPatterns => [NON_SHORTHAND_FIELD_PATTERNS]); |
c34b1796 | 237 | |
f035d41b XL |
238 | impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns { |
239 | fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) { | |
dfeec247 XL |
240 | if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind { |
241 | let variant = cx | |
3dfed10e | 242 | .typeck_results() |
dfeec247 XL |
243 | .pat_ty(pat) |
244 | .ty_adt_def() | |
245 | .expect("struct pattern type is not an ADT") | |
f035d41b | 246 | .variant_of_res(cx.qpath_res(qpath, pat.hir_id)); |
3157f602 | 247 | for fieldpat in field_pats { |
e1599b0c | 248 | if fieldpat.is_shorthand { |
3157f602 | 249 | continue; |
b039eaaf | 250 | } |
e1599b0c | 251 | if fieldpat.span.from_expansion() { |
83c7162d XL |
252 | // Don't lint if this is a macro expansion: macro authors |
253 | // shouldn't have to worry about this kind of style issue | |
254 | // (Issue #49588) | |
255 | continue; | |
256 | } | |
dfeec247 XL |
257 | if let PatKind::Binding(binding_annot, _, ident, None) = fieldpat.pat.kind { |
258 | if cx.tcx.find_field_index(ident, &variant) | |
3dfed10e | 259 | == Some(cx.tcx.field_index(fieldpat.hir_id, cx.typeck_results())) |
dfeec247 | 260 | { |
74b04a01 | 261 | cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span, |lint| { |
74b04a01 XL |
262 | let binding = match binding_annot { |
263 | hir::BindingAnnotation::Unannotated => None, | |
264 | hir::BindingAnnotation::Mutable => Some("mut"), | |
265 | hir::BindingAnnotation::Ref => Some("ref"), | |
266 | hir::BindingAnnotation::RefMut => Some("ref mut"), | |
267 | }; | |
064997fb | 268 | let suggested_ident = if let Some(binding) = binding { |
74b04a01 XL |
269 | format!("{} {}", binding, ident) |
270 | } else { | |
271 | ident.to_string() | |
272 | }; | |
064997fb FG |
273 | lint.build(fluent::lint::builtin_non_shorthand_field_patterns) |
274 | .set_arg("ident", ident.clone()) | |
275 | .span_suggestion( | |
276 | fieldpat.span, | |
277 | fluent::lint::suggestion, | |
278 | suggested_ident, | |
279 | Applicability::MachineApplicable, | |
280 | ) | |
281 | .emit(); | |
74b04a01 | 282 | }); |
c34b1796 AL |
283 | } |
284 | } | |
285 | } | |
286 | } | |
287 | } | |
288 | } | |
289 | ||
c34b1796 | 290 | declare_lint! { |
1b1a35ee XL |
291 | /// The `unsafe_code` lint catches usage of `unsafe` code. |
292 | /// | |
293 | /// ### Example | |
294 | /// | |
295 | /// ```rust,compile_fail | |
296 | /// #![deny(unsafe_code)] | |
297 | /// fn main() { | |
298 | /// unsafe { | |
299 | /// | |
300 | /// } | |
301 | /// } | |
302 | /// ``` | |
303 | /// | |
304 | /// {{produces}} | |
305 | /// | |
306 | /// ### Explanation | |
307 | /// | |
308 | /// This lint is intended to restrict the usage of `unsafe`, which can be | |
309 | /// difficult to use correctly. | |
c34b1796 AL |
310 | UNSAFE_CODE, |
311 | Allow, | |
312 | "usage of `unsafe` code" | |
313 | } | |
314 | ||
532ac7d7 | 315 | declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]); |
c34b1796 | 316 | |
3b2f2976 | 317 | impl UnsafeCode { |
74b04a01 XL |
318 | fn report_unsafe( |
319 | &self, | |
320 | cx: &EarlyContext<'_>, | |
321 | span: Span, | |
5e7ed085 | 322 | decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>), |
74b04a01 | 323 | ) { |
dc9dc135 | 324 | // This comes from a macro that has `#[allow_internal_unsafe]`. |
3b2f2976 XL |
325 | if span.allows_unsafe() { |
326 | return; | |
327 | } | |
328 | ||
74b04a01 | 329 | cx.struct_span_lint(UNSAFE_CODE, span, decorate); |
3b2f2976 | 330 | } |
6a06907d | 331 | |
064997fb FG |
332 | fn report_overridden_symbol_name( |
333 | &self, | |
334 | cx: &EarlyContext<'_>, | |
335 | span: Span, | |
336 | msg: DiagnosticMessage, | |
337 | ) { | |
6a06907d | 338 | self.report_unsafe(cx, span, |lint| { |
064997fb | 339 | lint.build(msg).note(fluent::lint::builtin_overridden_symbol_name).emit(); |
6a06907d XL |
340 | }) |
341 | } | |
923072b8 | 342 | |
064997fb FG |
343 | fn report_overridden_symbol_section( |
344 | &self, | |
345 | cx: &EarlyContext<'_>, | |
346 | span: Span, | |
347 | msg: DiagnosticMessage, | |
348 | ) { | |
923072b8 | 349 | self.report_unsafe(cx, span, |lint| { |
064997fb | 350 | lint.build(msg).note(fluent::lint::builtin_overridden_symbol_section).emit(); |
923072b8 FG |
351 | }) |
352 | } | |
3b2f2976 XL |
353 | } |
354 | ||
0731742a | 355 | impl EarlyLintPass for UnsafeCode { |
9fa01778 | 356 | fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { |
94222f64 | 357 | if attr.has_name(sym::allow_internal_unsafe) { |
74b04a01 | 358 | self.report_unsafe(cx, attr.span, |lint| { |
064997fb | 359 | lint.build(fluent::lint::builtin_allow_internal_unsafe).emit(); |
74b04a01 | 360 | }); |
0731742a XL |
361 | } |
362 | } | |
363 | ||
9fa01778 | 364 | fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { |
e74abb32 | 365 | if let ast::ExprKind::Block(ref blk, _) = e.kind { |
dc9dc135 | 366 | // Don't warn about generated blocks; that'll just pollute the output. |
0731742a | 367 | if blk.rules == ast::BlockCheckMode::Unsafe(ast::UserProvided) { |
74b04a01 | 368 | self.report_unsafe(cx, blk.span, |lint| { |
064997fb | 369 | lint.build(fluent::lint::builtin_unsafe_block).emit(); |
74b04a01 | 370 | }); |
c34b1796 AL |
371 | } |
372 | } | |
373 | } | |
374 | ||
9fa01778 | 375 | fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { |
e74abb32 | 376 | match it.kind { |
3c0e092e | 377 | ast::ItemKind::Trait(box ast::Trait { unsafety: ast::Unsafe::Yes(_), .. }) => self |
5869c6ff | 378 | .report_unsafe(cx, it.span, |lint| { |
064997fb | 379 | lint.build(fluent::lint::builtin_unsafe_trait).emit(); |
5869c6ff | 380 | }), |
c34b1796 | 381 | |
3c0e092e | 382 | ast::ItemKind::Impl(box ast::Impl { unsafety: ast::Unsafe::Yes(_), .. }) => self |
5869c6ff | 383 | .report_unsafe(cx, it.span, |lint| { |
064997fb | 384 | lint.build(fluent::lint::builtin_unsafe_impl).emit(); |
5869c6ff | 385 | }), |
c34b1796 | 386 | |
6a06907d XL |
387 | ast::ItemKind::Fn(..) => { |
388 | if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) { | |
5e7ed085 | 389 | self.report_overridden_symbol_name( |
6a06907d XL |
390 | cx, |
391 | attr.span, | |
064997fb | 392 | fluent::lint::builtin_no_mangle_fn, |
6a06907d XL |
393 | ); |
394 | } | |
923072b8 | 395 | |
6a06907d | 396 | if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) { |
5e7ed085 | 397 | self.report_overridden_symbol_name( |
6a06907d XL |
398 | cx, |
399 | attr.span, | |
064997fb | 400 | fluent::lint::builtin_export_name_fn, |
6a06907d XL |
401 | ); |
402 | } | |
923072b8 FG |
403 | |
404 | if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::link_section) { | |
405 | self.report_overridden_symbol_section( | |
406 | cx, | |
407 | attr.span, | |
064997fb | 408 | fluent::lint::builtin_link_section_fn, |
923072b8 FG |
409 | ); |
410 | } | |
6a06907d XL |
411 | } |
412 | ||
413 | ast::ItemKind::Static(..) => { | |
414 | if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) { | |
5e7ed085 | 415 | self.report_overridden_symbol_name( |
6a06907d XL |
416 | cx, |
417 | attr.span, | |
064997fb | 418 | fluent::lint::builtin_no_mangle_static, |
6a06907d XL |
419 | ); |
420 | } | |
923072b8 | 421 | |
6a06907d | 422 | if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) { |
5e7ed085 | 423 | self.report_overridden_symbol_name( |
6a06907d XL |
424 | cx, |
425 | attr.span, | |
064997fb | 426 | fluent::lint::builtin_export_name_static, |
6a06907d XL |
427 | ); |
428 | } | |
923072b8 FG |
429 | |
430 | if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::link_section) { | |
431 | self.report_overridden_symbol_section( | |
432 | cx, | |
433 | attr.span, | |
064997fb | 434 | fluent::lint::builtin_link_section_static, |
923072b8 FG |
435 | ); |
436 | } | |
6a06907d XL |
437 | } |
438 | ||
ba9703b0 | 439 | _ => {} |
c34b1796 AL |
440 | } |
441 | } | |
442 | ||
94222f64 XL |
443 | fn check_impl_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) { |
444 | if let ast::AssocItemKind::Fn(..) = it.kind { | |
445 | if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) { | |
5e7ed085 | 446 | self.report_overridden_symbol_name( |
94222f64 XL |
447 | cx, |
448 | attr.span, | |
064997fb | 449 | fluent::lint::builtin_no_mangle_method, |
94222f64 XL |
450 | ); |
451 | } | |
452 | if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) { | |
5e7ed085 | 453 | self.report_overridden_symbol_name( |
94222f64 XL |
454 | cx, |
455 | attr.span, | |
064997fb | 456 | fluent::lint::builtin_export_name_method, |
94222f64 XL |
457 | ); |
458 | } | |
459 | } | |
460 | } | |
461 | ||
74b04a01 XL |
462 | fn check_fn(&mut self, cx: &EarlyContext<'_>, fk: FnKind<'_>, span: Span, _: ast::NodeId) { |
463 | if let FnKind::Fn( | |
464 | ctxt, | |
465 | _, | |
466 | ast::FnSig { header: ast::FnHeader { unsafety: ast::Unsafe::Yes(_), .. }, .. }, | |
467 | _, | |
04454e1e | 468 | _, |
74b04a01 XL |
469 | body, |
470 | ) = fk | |
471 | { | |
472 | let msg = match ctxt { | |
473 | FnCtxt::Foreign => return, | |
064997fb FG |
474 | FnCtxt::Free => fluent::lint::builtin_decl_unsafe_fn, |
475 | FnCtxt::Assoc(_) if body.is_none() => fluent::lint::builtin_decl_unsafe_method, | |
476 | FnCtxt::Assoc(_) => fluent::lint::builtin_impl_unsafe_method, | |
74b04a01 | 477 | }; |
5e7ed085 FG |
478 | self.report_unsafe(cx, span, |lint| { |
479 | lint.build(msg).emit(); | |
480 | }); | |
c34b1796 AL |
481 | } |
482 | } | |
483 | } | |
484 | ||
c34b1796 | 485 | declare_lint! { |
1b1a35ee XL |
486 | /// The `missing_docs` lint detects missing documentation for public items. |
487 | /// | |
488 | /// ### Example | |
489 | /// | |
490 | /// ```rust,compile_fail | |
491 | /// #![deny(missing_docs)] | |
492 | /// pub fn foo() {} | |
493 | /// ``` | |
494 | /// | |
495 | /// {{produces}} | |
496 | /// | |
497 | /// ### Explanation | |
498 | /// | |
499 | /// This lint is intended to ensure that a library is well-documented. | |
500 | /// Items without documentation can be difficult for users to understand | |
501 | /// how to use properly. | |
502 | /// | |
503 | /// This lint is "allow" by default because it can be noisy, and not all | |
504 | /// projects may want to enforce everything to be documented. | |
94b46f34 | 505 | pub MISSING_DOCS, |
c34b1796 | 506 | Allow, |
8faf50e0 | 507 | "detects missing documentation for public members", |
e74abb32 | 508 | report_in_external_macro |
c34b1796 AL |
509 | } |
510 | ||
511 | pub struct MissingDoc { | |
9fa01778 | 512 | /// Stack of whether `#[doc(hidden)]` is set at each level which has lint attributes. |
c34b1796 | 513 | doc_hidden_stack: Vec<bool>, |
c34b1796 AL |
514 | } |
515 | ||
532ac7d7 XL |
516 | impl_lint_pass!(MissingDoc => [MISSING_DOCS]); |
517 | ||
94222f64 | 518 | fn has_doc(attr: &ast::Attribute) -> bool { |
dfeec247 XL |
519 | if attr.is_doc_comment() { |
520 | return true; | |
521 | } | |
522 | ||
94222f64 | 523 | if !attr.has_name(sym::doc) { |
0731742a XL |
524 | return false; |
525 | } | |
526 | ||
cdc7bbd5 | 527 | if attr.value_str().is_some() { |
0731742a XL |
528 | return true; |
529 | } | |
530 | ||
531 | if let Some(list) = attr.meta_item_list() { | |
532 | for meta in list { | |
17df50a5 | 533 | if meta.has_name(sym::hidden) { |
0731742a XL |
534 | return true; |
535 | } | |
536 | } | |
537 | } | |
538 | ||
539 | false | |
540 | } | |
541 | ||
c34b1796 AL |
542 | impl MissingDoc { |
543 | pub fn new() -> MissingDoc { | |
04454e1e | 544 | MissingDoc { doc_hidden_stack: vec![false] } |
c34b1796 AL |
545 | } |
546 | ||
547 | fn doc_hidden(&self) -> bool { | |
548 | *self.doc_hidden_stack.last().expect("empty doc_hidden_stack") | |
549 | } | |
550 | ||
dfeec247 XL |
551 | fn check_missing_docs_attrs( |
552 | &self, | |
f035d41b | 553 | cx: &LateContext<'_>, |
94222f64 | 554 | def_id: LocalDefId, |
ba9703b0 | 555 | article: &'static str, |
dfeec247 XL |
556 | desc: &'static str, |
557 | ) { | |
c34b1796 AL |
558 | // If we're building a test harness, then warning about |
559 | // documentation is probably not really relevant right now. | |
560 | if cx.sess().opts.test { | |
561 | return; | |
562 | } | |
563 | ||
564 | // `#[doc(hidden)]` disables missing_docs check. | |
565 | if self.doc_hidden() { | |
566 | return; | |
567 | } | |
568 | ||
569 | // Only check publicly-visible items, using the result from the privacy pass. | |
570 | // It's an option so the crate root can also use this function (it doesn't | |
dc9dc135 | 571 | // have a `NodeId`). |
94222f64 XL |
572 | if def_id != CRATE_DEF_ID { |
573 | if !cx.access_levels.is_exported(def_id) { | |
c34b1796 AL |
574 | return; |
575 | } | |
576 | } | |
577 | ||
04454e1e | 578 | let attrs = cx.tcx.hir().attrs(cx.tcx.hir().local_def_id_to_hir_id(def_id)); |
94222f64 | 579 | let has_doc = attrs.iter().any(has_doc); |
c34b1796 | 580 | if !has_doc { |
064997fb FG |
581 | cx.struct_span_lint(MISSING_DOCS, cx.tcx.def_span(def_id), |lint| { |
582 | lint.build(fluent::lint::builtin_missing_doc) | |
583 | .set_arg("article", article) | |
584 | .set_arg("desc", desc) | |
585 | .emit(); | |
586 | }); | |
c34b1796 AL |
587 | } |
588 | } | |
589 | } | |
590 | ||
f035d41b | 591 | impl<'tcx> LateLintPass<'tcx> for MissingDoc { |
94222f64 | 592 | fn enter_lint_attrs(&mut self, _cx: &LateContext<'_>, attrs: &[ast::Attribute]) { |
dfeec247 XL |
593 | let doc_hidden = self.doc_hidden() |
594 | || attrs.iter().any(|attr| { | |
94222f64 | 595 | attr.has_name(sym::doc) |
dfeec247 XL |
596 | && match attr.meta_item_list() { |
597 | None => false, | |
598 | Some(l) => attr::list_contains_name(&l, sym::hidden), | |
599 | } | |
600 | }); | |
c34b1796 AL |
601 | self.doc_hidden_stack.push(doc_hidden); |
602 | } | |
603 | ||
f035d41b | 604 | fn exit_lint_attrs(&mut self, _: &LateContext<'_>, _attrs: &[ast::Attribute]) { |
c34b1796 AL |
605 | self.doc_hidden_stack.pop().expect("empty doc_hidden_stack"); |
606 | } | |
607 | ||
c295e0f8 | 608 | fn check_crate(&mut self, cx: &LateContext<'_>) { |
064997fb | 609 | self.check_missing_docs_attrs(cx, CRATE_DEF_ID, "the", "crate"); |
c34b1796 AL |
610 | } |
611 | ||
f035d41b | 612 | fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { |
ba9703b0 | 613 | match it.kind { |
04454e1e | 614 | hir::ItemKind::Trait(..) => { |
dc9dc135 | 615 | // Issue #11592: traits are always considered exported, even when private. |
04454e1e FG |
616 | if cx.tcx.visibility(it.def_id) |
617 | == ty::Visibility::Restricted( | |
618 | cx.tcx.parent_module_from_def_id(it.def_id).to_def_id(), | |
619 | ) | |
620 | { | |
c30ab7b3 | 621 | return; |
c34b1796 | 622 | } |
c30ab7b3 | 623 | } |
ba9703b0 XL |
624 | hir::ItemKind::TyAlias(..) |
625 | | hir::ItemKind::Fn(..) | |
94222f64 | 626 | | hir::ItemKind::Macro(..) |
ba9703b0 XL |
627 | | hir::ItemKind::Mod(..) |
628 | | hir::ItemKind::Enum(..) | |
629 | | hir::ItemKind::Struct(..) | |
630 | | hir::ItemKind::Union(..) | |
631 | | hir::ItemKind::Const(..) | |
632 | | hir::ItemKind::Static(..) => {} | |
633 | ||
c30ab7b3 | 634 | _ => return, |
c34b1796 AL |
635 | }; |
636 | ||
6a06907d | 637 | let (article, desc) = cx.tcx.article_and_description(it.def_id.to_def_id()); |
ba9703b0 | 638 | |
064997fb | 639 | self.check_missing_docs_attrs(cx, it.def_id, article, desc); |
c34b1796 AL |
640 | } |
641 | ||
f035d41b | 642 | fn check_trait_item(&mut self, cx: &LateContext<'_>, trait_item: &hir::TraitItem<'_>) { |
6a06907d | 643 | let (article, desc) = cx.tcx.article_and_description(trait_item.def_id.to_def_id()); |
c34b1796 | 644 | |
064997fb | 645 | self.check_missing_docs_attrs(cx, trait_item.def_id, article, desc); |
c34b1796 AL |
646 | } |
647 | ||
f035d41b | 648 | fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { |
c34b1796 | 649 | // If the method is an impl for a trait, don't doc. |
6a06907d | 650 | if method_context(cx, impl_item.hir_id()) == MethodLateContext::TraitImpl { |
c34b1796 AL |
651 | return; |
652 | } | |
653 | ||
3c0e092e XL |
654 | // If the method is an impl for an item with docs_hidden, don't doc. |
655 | if method_context(cx, impl_item.hir_id()) == MethodLateContext::PlainImpl { | |
5099ac24 | 656 | let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()); |
3c0e092e XL |
657 | let impl_ty = cx.tcx.type_of(parent); |
658 | let outerdef = match impl_ty.kind() { | |
5e7ed085 | 659 | ty::Adt(def, _) => Some(def.did()), |
3c0e092e XL |
660 | ty::Foreign(def_id) => Some(*def_id), |
661 | _ => None, | |
662 | }; | |
663 | let is_hidden = match outerdef { | |
664 | Some(id) => cx.tcx.is_doc_hidden(id), | |
665 | None => false, | |
666 | }; | |
667 | if is_hidden { | |
668 | return; | |
669 | } | |
670 | } | |
671 | ||
6a06907d | 672 | let (article, desc) = cx.tcx.article_and_description(impl_item.def_id.to_def_id()); |
064997fb | 673 | self.check_missing_docs_attrs(cx, impl_item.def_id, article, desc); |
c34b1796 AL |
674 | } |
675 | ||
1b1a35ee | 676 | fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'_>) { |
6a06907d | 677 | let (article, desc) = cx.tcx.article_and_description(foreign_item.def_id.to_def_id()); |
064997fb | 678 | self.check_missing_docs_attrs(cx, foreign_item.def_id, article, desc); |
1b1a35ee XL |
679 | } |
680 | ||
6a06907d | 681 | fn check_field_def(&mut self, cx: &LateContext<'_>, sf: &hir::FieldDef<'_>) { |
54a0048b | 682 | if !sf.is_positional() { |
94222f64 | 683 | let def_id = cx.tcx.hir().local_def_id(sf.hir_id); |
064997fb | 684 | self.check_missing_docs_attrs(cx, def_id, "a", "struct field") |
c34b1796 AL |
685 | } |
686 | } | |
687 | ||
f035d41b | 688 | fn check_variant(&mut self, cx: &LateContext<'_>, v: &hir::Variant<'_>) { |
064997fb | 689 | self.check_missing_docs_attrs(cx, cx.tcx.hir().local_def_id(v.id), "a", "variant"); |
c34b1796 AL |
690 | } |
691 | } | |
692 | ||
693 | declare_lint! { | |
1b1a35ee XL |
694 | /// The `missing_copy_implementations` lint detects potentially-forgotten |
695 | /// implementations of [`Copy`]. | |
696 | /// | |
697 | /// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html | |
698 | /// | |
699 | /// ### Example | |
700 | /// | |
701 | /// ```rust,compile_fail | |
702 | /// #![deny(missing_copy_implementations)] | |
703 | /// pub struct Foo { | |
704 | /// pub field: i32 | |
705 | /// } | |
706 | /// # fn main() {} | |
707 | /// ``` | |
708 | /// | |
709 | /// {{produces}} | |
710 | /// | |
711 | /// ### Explanation | |
712 | /// | |
713 | /// Historically (before 1.0), types were automatically marked as `Copy` | |
714 | /// if possible. This was changed so that it required an explicit opt-in | |
715 | /// by implementing the `Copy` trait. As part of this change, a lint was | |
716 | /// added to alert if a copyable type was not marked `Copy`. | |
717 | /// | |
718 | /// This lint is "allow" by default because this code isn't bad; it is | |
719 | /// common to write newtypes like this specifically so that a `Copy` type | |
720 | /// is no longer `Copy`. `Copy` types can result in unintended copies of | |
721 | /// large data which can impact performance. | |
c34b1796 AL |
722 | pub MISSING_COPY_IMPLEMENTATIONS, |
723 | Allow, | |
724 | "detects potentially-forgotten implementations of `Copy`" | |
725 | } | |
726 | ||
532ac7d7 | 727 | declare_lint_pass!(MissingCopyImplementations => [MISSING_COPY_IMPLEMENTATIONS]); |
c34b1796 | 728 | |
f035d41b XL |
729 | impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { |
730 | fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { | |
94222f64 | 731 | if !cx.access_levels.is_reachable(item.def_id) { |
c34b1796 AL |
732 | return; |
733 | } | |
e74abb32 | 734 | let (def, ty) = match item.kind { |
8faf50e0 | 735 | hir::ItemKind::Struct(_, ref ast_generics) => { |
ff7c6d11 | 736 | if !ast_generics.params.is_empty() { |
c34b1796 AL |
737 | return; |
738 | } | |
6a06907d | 739 | let def = cx.tcx.adt_def(item.def_id); |
c30ab7b3 | 740 | (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[]))) |
9e0c209e | 741 | } |
8faf50e0 | 742 | hir::ItemKind::Union(_, ref ast_generics) => { |
ff7c6d11 | 743 | if !ast_generics.params.is_empty() { |
9e0c209e SL |
744 | return; |
745 | } | |
6a06907d | 746 | let def = cx.tcx.adt_def(item.def_id); |
c30ab7b3 | 747 | (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[]))) |
c34b1796 | 748 | } |
8faf50e0 | 749 | hir::ItemKind::Enum(_, ref ast_generics) => { |
ff7c6d11 | 750 | if !ast_generics.params.is_empty() { |
c34b1796 AL |
751 | return; |
752 | } | |
6a06907d | 753 | let def = cx.tcx.adt_def(item.def_id); |
c30ab7b3 | 754 | (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[]))) |
c34b1796 AL |
755 | } |
756 | _ => return, | |
757 | }; | |
8bb4bdeb | 758 | if def.has_dtor(cx.tcx) { |
c30ab7b3 SL |
759 | return; |
760 | } | |
0531ce1d | 761 | let param_env = ty::ParamEnv::empty(); |
f035d41b | 762 | if ty.is_copy_modulo_regions(cx.tcx.at(item.span), param_env) { |
c34b1796 AL |
763 | return; |
764 | } | |
5e7ed085 FG |
765 | if can_type_implement_copy( |
766 | cx.tcx, | |
767 | param_env, | |
768 | ty, | |
769 | traits::ObligationCause::misc(item.span, item.hir_id()), | |
770 | ) | |
771 | .is_ok() | |
772 | { | |
74b04a01 | 773 | cx.struct_span_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, |lint| { |
064997fb | 774 | lint.build(fluent::lint::builtin_missing_copy_impl).emit(); |
74b04a01 | 775 | }) |
c34b1796 AL |
776 | } |
777 | } | |
778 | } | |
779 | ||
780 | declare_lint! { | |
1b1a35ee XL |
781 | /// The `missing_debug_implementations` lint detects missing |
782 | /// implementations of [`fmt::Debug`]. | |
783 | /// | |
784 | /// [`fmt::Debug`]: https://doc.rust-lang.org/std/fmt/trait.Debug.html | |
785 | /// | |
786 | /// ### Example | |
787 | /// | |
788 | /// ```rust,compile_fail | |
789 | /// #![deny(missing_debug_implementations)] | |
790 | /// pub struct Foo; | |
791 | /// # fn main() {} | |
792 | /// ``` | |
793 | /// | |
794 | /// {{produces}} | |
795 | /// | |
796 | /// ### Explanation | |
797 | /// | |
798 | /// Having a `Debug` implementation on all types can assist with | |
799 | /// debugging, as it provides a convenient way to format and display a | |
800 | /// value. Using the `#[derive(Debug)]` attribute will automatically | |
801 | /// generate a typical implementation, or a custom implementation can be | |
802 | /// added by manually implementing the `Debug` trait. | |
803 | /// | |
804 | /// This lint is "allow" by default because adding `Debug` to all types can | |
805 | /// have a negative impact on compile time and code size. It also requires | |
806 | /// boilerplate to be added to every type, which can be an impediment. | |
c34b1796 AL |
807 | MISSING_DEBUG_IMPLEMENTATIONS, |
808 | Allow, | |
74b04a01 | 809 | "detects missing implementations of Debug" |
c34b1796 AL |
810 | } |
811 | ||
48663c56 | 812 | #[derive(Default)] |
c34b1796 | 813 | pub struct MissingDebugImplementations { |
6a06907d | 814 | impling_types: Option<LocalDefIdSet>, |
c34b1796 AL |
815 | } |
816 | ||
532ac7d7 XL |
817 | impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]); |
818 | ||
f035d41b XL |
819 | impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { |
820 | fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { | |
94222f64 | 821 | if !cx.access_levels.is_reachable(item.def_id) { |
c34b1796 AL |
822 | return; |
823 | } | |
824 | ||
e74abb32 | 825 | match item.kind { |
dfeec247 | 826 | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {} |
c34b1796 AL |
827 | _ => return, |
828 | } | |
829 | ||
a2a8927a XL |
830 | let Some(debug) = cx.tcx.get_diagnostic_item(sym::Debug) else { |
831 | return | |
c34b1796 AL |
832 | }; |
833 | ||
834 | if self.impling_types.is_none() { | |
6a06907d | 835 | let mut impls = LocalDefIdSet::default(); |
041b39d2 | 836 | cx.tcx.for_each_impl(debug, |d| { |
8faf50e0 | 837 | if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() { |
5e7ed085 | 838 | if let Some(def_id) = ty_def.did().as_local() { |
6a06907d | 839 | impls.insert(def_id); |
d9579d0f | 840 | } |
c34b1796 | 841 | } |
d9579d0f AL |
842 | }); |
843 | ||
c34b1796 AL |
844 | self.impling_types = Some(impls); |
845 | debug!("{:?}", self.impling_types); | |
846 | } | |
847 | ||
6a06907d | 848 | if !self.impling_types.as_ref().unwrap().contains(&item.def_id) { |
74b04a01 | 849 | cx.struct_span_lint(MISSING_DEBUG_IMPLEMENTATIONS, item.span, |lint| { |
064997fb FG |
850 | lint.build(fluent::lint::builtin_missing_debug_impl) |
851 | .set_arg("debug", cx.tcx.def_path_str(debug)) | |
852 | .emit(); | |
74b04a01 | 853 | }); |
c34b1796 AL |
854 | } |
855 | } | |
856 | } | |
857 | ||
7cac9316 | 858 | declare_lint! { |
1b1a35ee XL |
859 | /// The `anonymous_parameters` lint detects anonymous parameters in trait |
860 | /// definitions. | |
861 | /// | |
862 | /// ### Example | |
863 | /// | |
864 | /// ```rust,edition2015,compile_fail | |
865 | /// #![deny(anonymous_parameters)] | |
866 | /// // edition 2015 | |
867 | /// pub trait Foo { | |
868 | /// fn foo(usize); | |
869 | /// } | |
870 | /// fn main() {} | |
871 | /// ``` | |
872 | /// | |
873 | /// {{produces}} | |
874 | /// | |
875 | /// ### Explanation | |
876 | /// | |
877 | /// This syntax is mostly a historical accident, and can be worked around | |
878 | /// quite easily by adding an `_` pattern or a descriptive identifier: | |
879 | /// | |
880 | /// ```rust | |
881 | /// trait Foo { | |
882 | /// fn foo(_: usize); | |
883 | /// } | |
884 | /// ``` | |
885 | /// | |
886 | /// This syntax is now a hard error in the 2018 edition. In the 2015 | |
cdc7bbd5 | 887 | /// edition, this lint is "warn" by default. This lint |
1b1a35ee XL |
888 | /// enables the [`cargo fix`] tool with the `--edition` flag to |
889 | /// automatically transition old code from the 2015 edition to 2018. The | |
cdc7bbd5 | 890 | /// tool will run this lint and automatically apply the |
1b1a35ee XL |
891 | /// suggested fix from the compiler (which is to add `_` to each |
892 | /// parameter). This provides a completely automated way to update old | |
893 | /// code for a new edition. See [issue #41686] for more details. | |
894 | /// | |
895 | /// [issue #41686]: https://github.com/rust-lang/rust/issues/41686 | |
896 | /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html | |
7cac9316 | 897 | pub ANONYMOUS_PARAMETERS, |
cdc7bbd5 | 898 | Warn, |
e74abb32 XL |
899 | "detects anonymous parameters", |
900 | @future_incompatible = FutureIncompatibleInfo { | |
901 | reference: "issue #41686 <https://github.com/rust-lang/rust/issues/41686>", | |
136023e0 | 902 | reason: FutureIncompatibilityReason::EditionError(Edition::Edition2018), |
e74abb32 | 903 | }; |
7cac9316 XL |
904 | } |
905 | ||
532ac7d7 XL |
906 | declare_lint_pass!( |
907 | /// Checks for use of anonymous parameters (RFC 1685). | |
908 | AnonymousParameters => [ANONYMOUS_PARAMETERS] | |
909 | ); | |
7cac9316 XL |
910 | |
911 | impl EarlyLintPass for AnonymousParameters { | |
dfeec247 | 912 | fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) { |
5099ac24 | 913 | if cx.sess().edition() != Edition::Edition2015 { |
cdc7bbd5 XL |
914 | // This is a hard error in future editions; avoid linting and erroring |
915 | return; | |
916 | } | |
3c0e092e | 917 | if let ast::AssocItemKind::Fn(box Fn { ref sig, .. }) = it.kind { |
ba9703b0 XL |
918 | for arg in sig.decl.inputs.iter() { |
919 | if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind { | |
5869c6ff | 920 | if ident.name == kw::Empty { |
ba9703b0 | 921 | cx.struct_span_lint(ANONYMOUS_PARAMETERS, arg.pat.span, |lint| { |
5099ac24 | 922 | let ty_snip = cx.sess().source_map().span_to_snippet(arg.ty.span); |
ba9703b0 XL |
923 | |
924 | let (ty_snip, appl) = if let Ok(ref snip) = ty_snip { | |
925 | (snip.as_str(), Applicability::MachineApplicable) | |
926 | } else { | |
927 | ("<type>", Applicability::HasPlaceholders) | |
928 | }; | |
94b46f34 | 929 | |
064997fb FG |
930 | lint.build(fluent::lint::builtin_anonymous_params) |
931 | .span_suggestion( | |
932 | arg.pat.span, | |
933 | fluent::lint::suggestion, | |
934 | format!("_: {}", ty_snip), | |
935 | appl, | |
936 | ) | |
937 | .emit(); | |
ba9703b0 | 938 | }) |
7cac9316 XL |
939 | } |
940 | } | |
dfeec247 | 941 | } |
7cac9316 XL |
942 | } |
943 | } | |
944 | } | |
945 | ||
9fa01778 | 946 | /// Check for use of attributes which have been deprecated. |
c30ab7b3 SL |
947 | #[derive(Clone)] |
948 | pub struct DeprecatedAttr { | |
949 | // This is not free to compute, so we want to keep it around, rather than | |
950 | // compute it for every attribute. | |
3c0e092e | 951 | depr_attrs: Vec<&'static BuiltinAttribute>, |
c30ab7b3 SL |
952 | } |
953 | ||
532ac7d7 XL |
954 | impl_lint_pass!(DeprecatedAttr => []); |
955 | ||
c30ab7b3 SL |
956 | impl DeprecatedAttr { |
957 | pub fn new() -> DeprecatedAttr { | |
dfeec247 | 958 | DeprecatedAttr { depr_attrs: deprecated_attributes() } |
c30ab7b3 SL |
959 | } |
960 | } | |
961 | ||
c30ab7b3 | 962 | impl EarlyLintPass for DeprecatedAttr { |
9fa01778 | 963 | fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { |
3c0e092e XL |
964 | for BuiltinAttribute { name, gate, .. } in &self.depr_attrs { |
965 | if attr.ident().map(|ident| ident.name) == Some(*name) { | |
dfeec247 XL |
966 | if let &AttributeGate::Gated( |
967 | Stability::Deprecated(link, suggestion), | |
5869c6ff XL |
968 | name, |
969 | reason, | |
dfeec247 | 970 | _, |
3c0e092e | 971 | ) = gate |
dfeec247 | 972 | { |
064997fb FG |
973 | cx.struct_span_lint(DEPRECATED, attr.span, |lint| { |
974 | // FIXME(davidtwco) translatable deprecated attr | |
975 | lint.build(fluent::lint::builtin_deprecated_attr_link) | |
976 | .set_arg("name", name) | |
977 | .set_arg("reason", reason) | |
978 | .set_arg("link", link) | |
979 | .span_suggestion_short( | |
980 | attr.span, | |
981 | suggestion.map(|s| s.into()).unwrap_or( | |
982 | fluent::lint::builtin_deprecated_attr_default_suggestion, | |
983 | ), | |
984 | "", | |
985 | Applicability::MachineApplicable, | |
986 | ) | |
987 | .emit(); | |
988 | }); | |
c30ab7b3 SL |
989 | } |
990 | return; | |
991 | } | |
992 | } | |
94222f64 | 993 | if attr.has_name(sym::no_start) || attr.has_name(sym::crate_id) { |
064997fb FG |
994 | cx.struct_span_lint(DEPRECATED, attr.span, |lint| { |
995 | lint.build(fluent::lint::builtin_deprecated_attr_used) | |
996 | .set_arg("name", pprust::path_to_string(&attr.get_normal_item().path)) | |
997 | .span_suggestion_short( | |
998 | attr.span, | |
999 | fluent::lint::builtin_deprecated_attr_default_suggestion, | |
1000 | "", | |
1001 | Applicability::MachineApplicable, | |
1002 | ) | |
1003 | .emit(); | |
1004 | }); | |
e1599b0c | 1005 | } |
c30ab7b3 SL |
1006 | } |
1007 | } | |
1008 | ||
74b04a01 | 1009 | fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &[ast::Attribute]) { |
136023e0 XL |
1010 | use rustc_ast::token::CommentKind; |
1011 | ||
74b04a01 | 1012 | let mut attrs = attrs.iter().peekable(); |
3b2f2976 | 1013 | |
74b04a01 XL |
1014 | // Accumulate a single span for sugared doc comments. |
1015 | let mut sugared_span: Option<Span> = None; | |
3b2f2976 | 1016 | |
74b04a01 | 1017 | while let Some(attr) = attrs.next() { |
136023e0 XL |
1018 | let is_doc_comment = attr.is_doc_comment(); |
1019 | if is_doc_comment { | |
74b04a01 | 1020 | sugared_span = |
29967ef6 | 1021 | Some(sugared_span.map_or(attr.span, |span| span.with_hi(attr.span.hi()))); |
74b04a01 | 1022 | } |
3b2f2976 | 1023 | |
cdc7bbd5 | 1024 | if attrs.peek().map_or(false, |next_attr| next_attr.is_doc_comment()) { |
74b04a01 XL |
1025 | continue; |
1026 | } | |
532ac7d7 | 1027 | |
1b1a35ee | 1028 | let span = sugared_span.take().unwrap_or(attr.span); |
532ac7d7 | 1029 | |
94222f64 | 1030 | if is_doc_comment || attr.has_name(sym::doc) { |
74b04a01 | 1031 | cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, |lint| { |
064997fb FG |
1032 | let mut err = lint.build(fluent::lint::builtin_unused_doc_comment); |
1033 | err.set_arg("kind", node_kind); | |
1034 | err.span_label(node_span, fluent::lint::label); | |
136023e0 XL |
1035 | match attr.kind { |
1036 | AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => { | |
064997fb | 1037 | err.help(fluent::lint::plain_help); |
136023e0 XL |
1038 | } |
1039 | AttrKind::DocComment(CommentKind::Block, _) => { | |
064997fb | 1040 | err.help(fluent::lint::block_help); |
136023e0 XL |
1041 | } |
1042 | } | |
532ac7d7 | 1043 | err.emit(); |
74b04a01 | 1044 | }); |
3b2f2976 XL |
1045 | } |
1046 | } | |
1047 | } | |
1048 | ||
1049 | impl EarlyLintPass for UnusedDocComment { | |
532ac7d7 | 1050 | fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { |
74b04a01 XL |
1051 | let kind = match stmt.kind { |
1052 | ast::StmtKind::Local(..) => "statements", | |
29967ef6 XL |
1053 | // Disabled pending discussion in #78306 |
1054 | ast::StmtKind::Item(..) => return, | |
532ac7d7 | 1055 | // expressions will be reported by `check_expr`. |
74b04a01 XL |
1056 | ast::StmtKind::Empty |
1057 | | ast::StmtKind::Semi(_) | |
1058 | | ast::StmtKind::Expr(_) | |
ba9703b0 | 1059 | | ast::StmtKind::MacCall(_) => return, |
532ac7d7 XL |
1060 | }; |
1061 | ||
74b04a01 | 1062 | warn_if_doc(cx, stmt.span, kind, stmt.kind.attrs()); |
3b2f2976 XL |
1063 | } |
1064 | ||
9fa01778 | 1065 | fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) { |
e1599b0c | 1066 | let arm_span = arm.pat.span.with_hi(arm.body.span.hi()); |
74b04a01 | 1067 | warn_if_doc(cx, arm_span, "match arms", &arm.attrs); |
3b2f2976 XL |
1068 | } |
1069 | ||
9fa01778 | 1070 | fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { |
74b04a01 | 1071 | warn_if_doc(cx, expr.span, "expressions", &expr.attrs); |
3b2f2976 | 1072 | } |
a2a8927a XL |
1073 | |
1074 | fn check_generic_param(&mut self, cx: &EarlyContext<'_>, param: &ast::GenericParam) { | |
1075 | warn_if_doc(cx, param.ident.span, "generic parameters", ¶m.attrs); | |
1076 | } | |
5e7ed085 FG |
1077 | |
1078 | fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { | |
064997fb | 1079 | warn_if_doc(cx, block.span, "blocks", &block.attrs()); |
5e7ed085 FG |
1080 | } |
1081 | ||
1082 | fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { | |
1083 | if let ast::ItemKind::ForeignMod(_) = item.kind { | |
064997fb | 1084 | warn_if_doc(cx, item.span, "extern blocks", &item.attrs); |
5e7ed085 FG |
1085 | } |
1086 | } | |
3b2f2976 XL |
1087 | } |
1088 | ||
c34b1796 | 1089 | declare_lint! { |
1b1a35ee XL |
1090 | /// The `no_mangle_const_items` lint detects any `const` items with the |
1091 | /// [`no_mangle` attribute]. | |
1092 | /// | |
1093 | /// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute | |
1094 | /// | |
1095 | /// ### Example | |
1096 | /// | |
1097 | /// ```rust,compile_fail | |
1098 | /// #[no_mangle] | |
1099 | /// const FOO: i32 = 5; | |
1100 | /// ``` | |
1101 | /// | |
1102 | /// {{produces}} | |
1103 | /// | |
1104 | /// ### Explanation | |
1105 | /// | |
1106 | /// Constants do not have their symbols exported, and therefore, this | |
1107 | /// probably means you meant to use a [`static`], not a [`const`]. | |
1108 | /// | |
1109 | /// [`static`]: https://doc.rust-lang.org/reference/items/static-items.html | |
1110 | /// [`const`]: https://doc.rust-lang.org/reference/items/constant-items.html | |
c34b1796 AL |
1111 | NO_MANGLE_CONST_ITEMS, |
1112 | Deny, | |
1113 | "const items will not have their symbols exported" | |
1114 | } | |
1115 | ||
9cc50fc6 | 1116 | declare_lint! { |
1b1a35ee XL |
1117 | /// The `no_mangle_generic_items` lint detects generic items that must be |
1118 | /// mangled. | |
1119 | /// | |
1120 | /// ### Example | |
1121 | /// | |
1122 | /// ```rust | |
1123 | /// #[no_mangle] | |
1124 | /// fn foo<T>(t: T) { | |
1125 | /// | |
1126 | /// } | |
1127 | /// ``` | |
1128 | /// | |
1129 | /// {{produces}} | |
1130 | /// | |
1131 | /// ### Explanation | |
1132 | /// | |
136023e0 | 1133 | /// A function with generics must have its symbol mangled to accommodate |
1b1a35ee XL |
1134 | /// the generic parameter. The [`no_mangle` attribute] has no effect in |
1135 | /// this situation, and should be removed. | |
1136 | /// | |
1137 | /// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute | |
9cc50fc6 SL |
1138 | NO_MANGLE_GENERIC_ITEMS, |
1139 | Warn, | |
1140 | "generic items must be mangled" | |
1141 | } | |
1142 | ||
532ac7d7 | 1143 | declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GENERIC_ITEMS]); |
c34b1796 | 1144 | |
f035d41b XL |
1145 | impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { |
1146 | fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { | |
6a06907d | 1147 | let attrs = cx.tcx.hir().attrs(it.hir_id()); |
94222f64 XL |
1148 | let check_no_mangle_on_generic_fn = |no_mangle_attr: &ast::Attribute, |
1149 | impl_generics: Option<&hir::Generics<'_>>, | |
1150 | generics: &hir::Generics<'_>, | |
1151 | span| { | |
1152 | for param in | |
1153 | generics.params.iter().chain(impl_generics.map(|g| g.params).into_iter().flatten()) | |
1154 | { | |
1155 | match param.kind { | |
1156 | GenericParamKind::Lifetime { .. } => {} | |
1157 | GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { | |
1158 | cx.struct_span_lint(NO_MANGLE_GENERIC_ITEMS, span, |lint| { | |
064997fb | 1159 | lint.build(fluent::lint::builtin_no_mangle_generic) |
94222f64 XL |
1160 | .span_suggestion_short( |
1161 | no_mangle_attr.span, | |
064997fb | 1162 | fluent::lint::suggestion, |
923072b8 | 1163 | "", |
94222f64 XL |
1164 | // Use of `#[no_mangle]` suggests FFI intent; correct |
1165 | // fix may be to monomorphize source by hand | |
1166 | Applicability::MaybeIncorrect, | |
1167 | ) | |
1168 | .emit(); | |
1169 | }); | |
1170 | break; | |
1171 | } | |
1172 | } | |
1173 | } | |
1174 | }; | |
e74abb32 | 1175 | match it.kind { |
8faf50e0 | 1176 | hir::ItemKind::Fn(.., ref generics, _) => { |
6a06907d | 1177 | if let Some(no_mangle_attr) = cx.sess().find_by_name(attrs, sym::no_mangle) { |
94222f64 | 1178 | check_no_mangle_on_generic_fn(no_mangle_attr, None, generics, it.span); |
c34b1796 | 1179 | } |
c30ab7b3 | 1180 | } |
8faf50e0 | 1181 | hir::ItemKind::Const(..) => { |
6a06907d | 1182 | if cx.sess().contains_name(attrs, sym::no_mangle) { |
c34b1796 AL |
1183 | // Const items do not refer to a particular location in memory, and therefore |
1184 | // don't have anything to attach a symbol to | |
74b04a01 | 1185 | cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, |lint| { |
064997fb | 1186 | let mut err = lint.build(fluent::lint::builtin_const_no_mangle); |
74b04a01 XL |
1187 | |
1188 | // account for "pub const" (#45562) | |
1189 | let start = cx | |
1190 | .tcx | |
1191 | .sess | |
1192 | .source_map() | |
1193 | .span_to_snippet(it.span) | |
1194 | .map(|snippet| snippet.find("const").unwrap_or(0)) | |
1195 | .unwrap_or(0) as u32; | |
1196 | // `const` is 5 chars | |
1197 | let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5)); | |
1198 | err.span_suggestion( | |
1199 | const_span, | |
064997fb | 1200 | fluent::lint::suggestion, |
923072b8 | 1201 | "pub static", |
74b04a01 XL |
1202 | Applicability::MachineApplicable, |
1203 | ); | |
1204 | err.emit(); | |
1205 | }); | |
c34b1796 AL |
1206 | } |
1207 | } | |
04454e1e FG |
1208 | hir::ItemKind::Impl(hir::Impl { generics, items, .. }) => { |
1209 | for it in *items { | |
94222f64 XL |
1210 | if let hir::AssocItemKind::Fn { .. } = it.kind { |
1211 | if let Some(no_mangle_attr) = cx | |
1212 | .sess() | |
1213 | .find_by_name(cx.tcx.hir().attrs(it.id.hir_id()), sym::no_mangle) | |
1214 | { | |
1215 | check_no_mangle_on_generic_fn( | |
1216 | no_mangle_attr, | |
1217 | Some(generics), | |
5099ac24 | 1218 | cx.tcx.hir().get_generics(it.id.def_id).unwrap(), |
94222f64 XL |
1219 | it.span, |
1220 | ); | |
1221 | } | |
1222 | } | |
1223 | } | |
1224 | } | |
c30ab7b3 | 1225 | _ => {} |
c34b1796 AL |
1226 | } |
1227 | } | |
1228 | } | |
1229 | ||
bd371182 | 1230 | declare_lint! { |
1b1a35ee XL |
1231 | /// The `mutable_transmutes` lint catches transmuting from `&T` to `&mut |
1232 | /// T` because it is [undefined behavior]. | |
1233 | /// | |
1234 | /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html | |
1235 | /// | |
1236 | /// ### Example | |
1237 | /// | |
1238 | /// ```rust,compile_fail | |
1239 | /// unsafe { | |
1240 | /// let y = std::mem::transmute::<&i32, &mut i32>(&5); | |
1241 | /// } | |
1242 | /// ``` | |
1243 | /// | |
1244 | /// {{produces}} | |
1245 | /// | |
1246 | /// ### Explanation | |
1247 | /// | |
1248 | /// Certain assumptions are made about aliasing of data, and this transmute | |
1249 | /// violates those assumptions. Consider using [`UnsafeCell`] instead. | |
1250 | /// | |
1251 | /// [`UnsafeCell`]: https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html | |
bd371182 AL |
1252 | MUTABLE_TRANSMUTES, |
1253 | Deny, | |
5099ac24 | 1254 | "transmuting &T to &mut T is undefined behavior, even if the reference is unused" |
bd371182 AL |
1255 | } |
1256 | ||
532ac7d7 | 1257 | declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]); |
bd371182 | 1258 | |
f035d41b XL |
1259 | impl<'tcx> LateLintPass<'tcx> for MutableTransmutes { |
1260 | fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { | |
ba9703b0 | 1261 | if let Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) = |
1b1a35ee | 1262 | get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind())) |
ba9703b0 XL |
1263 | { |
1264 | if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not { | |
5e7ed085 | 1265 | cx.struct_span_lint(MUTABLE_TRANSMUTES, expr.span, |lint| { |
064997fb | 1266 | lint.build(fluent::lint::builtin_mutable_transmutes).emit(); |
5e7ed085 | 1267 | }); |
bd371182 | 1268 | } |
bd371182 AL |
1269 | } |
1270 | ||
f035d41b XL |
1271 | fn get_transmute_from_to<'tcx>( |
1272 | cx: &LateContext<'tcx>, | |
dfeec247 XL |
1273 | expr: &hir::Expr<'_>, |
1274 | ) -> Option<(Ty<'tcx>, Ty<'tcx>)> { | |
e74abb32 | 1275 | let def = if let hir::ExprKind::Path(ref qpath) = expr.kind { |
f035d41b | 1276 | cx.qpath_res(qpath, expr.hir_id) |
476ff2be SL |
1277 | } else { |
1278 | return None; | |
1279 | }; | |
48663c56 | 1280 | if let Res::Def(DefKind::Fn, did) = def { |
bd371182 AL |
1281 | if !def_id_is_transmute(cx, did) { |
1282 | return None; | |
1283 | } | |
3dfed10e | 1284 | let sig = cx.typeck_results().node_type(expr.hir_id).fn_sig(cx.tcx); |
041b39d2 | 1285 | let from = sig.inputs().skip_binder()[0]; |
f035d41b | 1286 | let to = sig.output().skip_binder(); |
532ac7d7 | 1287 | return Some((from, to)); |
bd371182 AL |
1288 | } |
1289 | None | |
1290 | } | |
1291 | ||
f035d41b | 1292 | fn def_id_is_transmute(cx: &LateContext<'_>, def_id: DefId) -> bool { |
923072b8 | 1293 | cx.tcx.is_intrinsic(def_id) && cx.tcx.item_name(def_id) == sym::transmute |
bd371182 AL |
1294 | } |
1295 | } | |
1296 | } | |
1297 | ||
c34b1796 | 1298 | declare_lint! { |
1b1a35ee | 1299 | /// The `unstable_features` is deprecated and should no longer be used. |
c34b1796 AL |
1300 | UNSTABLE_FEATURES, |
1301 | Allow, | |
62682a34 | 1302 | "enabling unstable features (deprecated. do not use)" |
c34b1796 AL |
1303 | } |
1304 | ||
532ac7d7 XL |
1305 | declare_lint_pass!( |
1306 | /// Forbids using the `#[feature(...)]` attribute | |
1307 | UnstableFeatures => [UNSTABLE_FEATURES] | |
1308 | ); | |
b039eaaf | 1309 | |
f035d41b | 1310 | impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { |
3dfed10e | 1311 | fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { |
94222f64 | 1312 | if attr.has_name(sym::feature) { |
cc61c64b | 1313 | if let Some(items) = attr.meta_item_list() { |
62682a34 | 1314 | for item in items { |
3dfed10e | 1315 | cx.struct_span_lint(UNSTABLE_FEATURES, item.span(), |lint| { |
064997fb | 1316 | lint.build(fluent::lint::builtin_unstable_features).emit(); |
74b04a01 | 1317 | }); |
62682a34 SL |
1318 | } |
1319 | } | |
c34b1796 AL |
1320 | } |
1321 | } | |
1322 | } | |
bd371182 | 1323 | |
abe05a73 | 1324 | declare_lint! { |
1b1a35ee XL |
1325 | /// The `unreachable_pub` lint triggers for `pub` items not reachable from |
1326 | /// the crate root. | |
1327 | /// | |
1328 | /// ### Example | |
1329 | /// | |
1330 | /// ```rust,compile_fail | |
1331 | /// #![deny(unreachable_pub)] | |
1332 | /// mod foo { | |
1333 | /// pub mod bar { | |
1334 | /// | |
1335 | /// } | |
1336 | /// } | |
1337 | /// ``` | |
1338 | /// | |
1339 | /// {{produces}} | |
1340 | /// | |
1341 | /// ### Explanation | |
1342 | /// | |
1343 | /// A bare `pub` visibility may be misleading if the item is not actually | |
1344 | /// publicly exported from the crate. The `pub(crate)` visibility is | |
1345 | /// recommended to be used instead, which more clearly expresses the intent | |
1346 | /// that the item is only visible within its own crate. | |
1347 | /// | |
1348 | /// This lint is "allow" by default because it will trigger for a large | |
1349 | /// amount existing Rust code, and has some false-positives. Eventually it | |
1350 | /// is desired for this to become warn-by-default. | |
0531ce1d | 1351 | pub UNREACHABLE_PUB, |
abe05a73 XL |
1352 | Allow, |
1353 | "`pub` items not reachable from crate root" | |
1354 | } | |
1355 | ||
532ac7d7 XL |
1356 | declare_lint_pass!( |
1357 | /// Lint for items marked `pub` that aren't reachable from other crates. | |
1358 | UnreachablePub => [UNREACHABLE_PUB] | |
1359 | ); | |
abe05a73 XL |
1360 | |
1361 | impl UnreachablePub { | |
dfeec247 XL |
1362 | fn perform_lint( |
1363 | &self, | |
f035d41b | 1364 | cx: &LateContext<'_>, |
dfeec247 | 1365 | what: &str, |
94222f64 | 1366 | def_id: LocalDefId, |
04454e1e | 1367 | vis_span: Span, |
dfeec247 XL |
1368 | exportable: bool, |
1369 | ) { | |
8faf50e0 | 1370 | let mut applicability = Applicability::MachineApplicable; |
04454e1e FG |
1371 | if cx.tcx.visibility(def_id).is_public() && !cx.access_levels.is_reachable(def_id) { |
1372 | if vis_span.from_expansion() { | |
1373 | applicability = Applicability::MaybeIncorrect; | |
1374 | } | |
064997fb | 1375 | let def_span = cx.tcx.def_span(def_id); |
04454e1e | 1376 | cx.struct_span_lint(UNREACHABLE_PUB, def_span, |lint| { |
064997fb FG |
1377 | let mut err = lint.build(fluent::lint::builtin_unreachable_pub); |
1378 | err.set_arg("what", what); | |
8faf50e0 | 1379 | |
04454e1e FG |
1380 | err.span_suggestion( |
1381 | vis_span, | |
064997fb | 1382 | fluent::lint::suggestion, |
923072b8 | 1383 | "pub(crate)", |
04454e1e FG |
1384 | applicability, |
1385 | ); | |
1386 | if exportable { | |
064997fb | 1387 | err.help(fluent::lint::help); |
04454e1e FG |
1388 | } |
1389 | err.emit(); | |
1390 | }); | |
abe05a73 XL |
1391 | } |
1392 | } | |
1393 | } | |
1394 | ||
f035d41b XL |
1395 | impl<'tcx> LateLintPass<'tcx> for UnreachablePub { |
1396 | fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { | |
04454e1e FG |
1397 | // Do not warn for fake `use` statements. |
1398 | if let hir::ItemKind::Use(_, hir::UseKind::ListStem) = &item.kind { | |
1399 | return; | |
1400 | } | |
064997fb | 1401 | self.perform_lint(cx, "item", item.def_id, item.vis_span, true); |
abe05a73 XL |
1402 | } |
1403 | ||
f035d41b | 1404 | fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'tcx>) { |
064997fb | 1405 | self.perform_lint(cx, "item", foreign_item.def_id, foreign_item.vis_span, true); |
abe05a73 XL |
1406 | } |
1407 | ||
6a06907d | 1408 | fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { |
94222f64 | 1409 | let def_id = cx.tcx.hir().local_def_id(field.hir_id); |
064997fb | 1410 | self.perform_lint(cx, "field", def_id, field.vis_span, false); |
abe05a73 XL |
1411 | } |
1412 | ||
f035d41b | 1413 | fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { |
04454e1e FG |
1414 | // Only lint inherent impl items. |
1415 | if cx.tcx.associated_item(impl_item.def_id).trait_item_def_id.is_none() { | |
064997fb | 1416 | self.perform_lint(cx, "item", impl_item.def_id, impl_item.vis_span, false); |
04454e1e | 1417 | } |
abe05a73 XL |
1418 | } |
1419 | } | |
0531ce1d | 1420 | |
0531ce1d | 1421 | declare_lint! { |
1b1a35ee XL |
1422 | /// The `type_alias_bounds` lint detects bounds in type aliases. |
1423 | /// | |
1424 | /// ### Example | |
1425 | /// | |
1426 | /// ```rust | |
1427 | /// type SendVec<T: Send> = Vec<T>; | |
1428 | /// ``` | |
1429 | /// | |
1430 | /// {{produces}} | |
1431 | /// | |
1432 | /// ### Explanation | |
1433 | /// | |
1434 | /// The trait bounds in a type alias are currently ignored, and should not | |
1435 | /// be included to avoid confusion. This was previously allowed | |
1436 | /// unintentionally; this may become a hard error in the future. | |
0531ce1d XL |
1437 | TYPE_ALIAS_BOUNDS, |
1438 | Warn, | |
1439 | "bounds in type aliases are not enforced" | |
1440 | } | |
1441 | ||
532ac7d7 XL |
1442 | declare_lint_pass!( |
1443 | /// Lint for trait and lifetime bounds in type aliases being mostly ignored. | |
1444 | /// They are relevant when using associated types, but otherwise neither checked | |
1445 | /// at definition site nor enforced at use site. | |
1446 | TypeAliasBounds => [TYPE_ALIAS_BOUNDS] | |
1447 | ); | |
0531ce1d XL |
1448 | |
1449 | impl TypeAliasBounds { | |
dfeec247 | 1450 | fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool { |
0531ce1d XL |
1451 | match *qpath { |
1452 | hir::QPath::TypeRelative(ref ty, _) => { | |
1453 | // If this is a type variable, we found a `T::Assoc`. | |
e74abb32 | 1454 | match ty.kind { |
29967ef6 XL |
1455 | hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => { |
1456 | matches!(path.res, Res::Def(DefKind::TyParam, _)) | |
1457 | } | |
dfeec247 | 1458 | _ => false, |
0531ce1d XL |
1459 | } |
1460 | } | |
3dfed10e | 1461 | hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false, |
0531ce1d XL |
1462 | } |
1463 | } | |
1464 | ||
5e7ed085 | 1465 | fn suggest_changing_assoc_types(ty: &hir::Ty<'_>, err: &mut Diagnostic) { |
0531ce1d XL |
1466 | // Access to associates types should use `<T as Bound>::Assoc`, which does not need a |
1467 | // bound. Let's see if this type does that. | |
1468 | ||
1469 | // We use a HIR visitor to walk the type. | |
dfeec247 | 1470 | use rustc_hir::intravisit::{self, Visitor}; |
5e7ed085 FG |
1471 | struct WalkAssocTypes<'a> { |
1472 | err: &'a mut Diagnostic, | |
0531ce1d | 1473 | } |
5e7ed085 FG |
1474 | impl Visitor<'_> for WalkAssocTypes<'_> { |
1475 | fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) { | |
0531ce1d | 1476 | if TypeAliasBounds::is_type_variable_assoc(qpath) { |
064997fb | 1477 | self.err.span_help(span, fluent::lint::builtin_type_alias_bounds_help); |
0531ce1d XL |
1478 | } |
1479 | intravisit::walk_qpath(self, qpath, id, span) | |
1480 | } | |
1481 | } | |
1482 | ||
1483 | // Let's go for a walk! | |
1484 | let mut visitor = WalkAssocTypes { err }; | |
1485 | visitor.visit_ty(ty); | |
1486 | } | |
1487 | } | |
1488 | ||
f035d41b XL |
1489 | impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { |
1490 | fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { | |
a2a8927a XL |
1491 | let hir::ItemKind::TyAlias(ty, type_alias_generics) = &item.kind else { |
1492 | return | |
0531ce1d | 1493 | }; |
f035d41b XL |
1494 | if let hir::TyKind::OpaqueDef(..) = ty.kind { |
1495 | // Bounds are respected for `type X = impl Trait` | |
1496 | return; | |
1497 | } | |
0531ce1d | 1498 | // There must not be a where clause |
04454e1e FG |
1499 | if type_alias_generics.predicates.is_empty() { |
1500 | return; | |
0531ce1d | 1501 | } |
04454e1e FG |
1502 | |
1503 | let mut where_spans = Vec::new(); | |
1504 | let mut inline_spans = Vec::new(); | |
1505 | let mut inline_sugg = Vec::new(); | |
1506 | for p in type_alias_generics.predicates { | |
1507 | let span = p.span(); | |
1508 | if p.in_where_clause() { | |
1509 | where_spans.push(span); | |
1510 | } else { | |
1511 | for b in p.bounds() { | |
1512 | inline_spans.push(b.span()); | |
1513 | } | |
1514 | inline_sugg.push((span, String::new())); | |
0531ce1d XL |
1515 | } |
1516 | } | |
04454e1e FG |
1517 | |
1518 | let mut suggested_changing_assoc_types = false; | |
1519 | if !where_spans.is_empty() { | |
1520 | cx.lint(TYPE_ALIAS_BOUNDS, |lint| { | |
064997fb | 1521 | let mut err = lint.build(fluent::lint::builtin_type_alias_where_clause); |
04454e1e FG |
1522 | err.set_span(where_spans); |
1523 | err.span_suggestion( | |
1524 | type_alias_generics.where_clause_span, | |
064997fb | 1525 | fluent::lint::suggestion, |
923072b8 | 1526 | "", |
04454e1e FG |
1527 | Applicability::MachineApplicable, |
1528 | ); | |
1529 | if !suggested_changing_assoc_types { | |
1530 | TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); | |
1531 | suggested_changing_assoc_types = true; | |
1532 | } | |
1533 | err.emit(); | |
1534 | }); | |
1535 | } | |
1536 | ||
1537 | if !inline_spans.is_empty() { | |
1538 | cx.lint(TYPE_ALIAS_BOUNDS, |lint| { | |
064997fb | 1539 | let mut err = lint.build(fluent::lint::builtin_type_alias_generic_bounds); |
04454e1e FG |
1540 | err.set_span(inline_spans); |
1541 | err.multipart_suggestion( | |
064997fb | 1542 | fluent::lint::suggestion, |
04454e1e FG |
1543 | inline_sugg, |
1544 | Applicability::MachineApplicable, | |
1545 | ); | |
1546 | if !suggested_changing_assoc_types { | |
1547 | TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); | |
1548 | } | |
1549 | err.emit(); | |
1550 | }); | |
1551 | } | |
0531ce1d XL |
1552 | } |
1553 | } | |
1554 | ||
532ac7d7 XL |
1555 | declare_lint_pass!( |
1556 | /// Lint constants that are erroneous. | |
1557 | /// Without this lint, we might not get any diagnostic if the constant is | |
1558 | /// unused within this crate, even though downstream crates can't use it | |
1559 | /// without producing an error. | |
1560 | UnusedBrokenConst => [] | |
1561 | ); | |
9fa01778 | 1562 | |
f035d41b XL |
1563 | impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst { |
1564 | fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { | |
e74abb32 | 1565 | match it.kind { |
8faf50e0 | 1566 | hir::ItemKind::Const(_, body_id) => { |
1b1a35ee XL |
1567 | let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id(); |
1568 | // trigger the query once for all constants since that will already report the errors | |
923072b8 | 1569 | cx.tcx.ensure().const_eval_poly(def_id); |
dfeec247 | 1570 | } |
8faf50e0 | 1571 | hir::ItemKind::Static(_, _, body_id) => { |
1b1a35ee | 1572 | let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id(); |
923072b8 | 1573 | cx.tcx.ensure().eval_static_initializer(def_id); |
dfeec247 XL |
1574 | } |
1575 | _ => {} | |
0531ce1d XL |
1576 | } |
1577 | } | |
1578 | } | |
94b46f34 | 1579 | |
94b46f34 | 1580 | declare_lint! { |
1b1a35ee XL |
1581 | /// The `trivial_bounds` lint detects trait bounds that don't depend on |
1582 | /// any type parameters. | |
1583 | /// | |
1584 | /// ### Example | |
1585 | /// | |
1586 | /// ```rust | |
1587 | /// #![feature(trivial_bounds)] | |
1588 | /// pub struct A where i32: Copy; | |
1589 | /// ``` | |
1590 | /// | |
1591 | /// {{produces}} | |
1592 | /// | |
1593 | /// ### Explanation | |
1594 | /// | |
1595 | /// Usually you would not write a trait bound that you know is always | |
1596 | /// true, or never true. However, when using macros, the macro may not | |
1597 | /// know whether or not the constraint would hold or not at the time when | |
1598 | /// generating the code. Currently, the compiler does not alert you if the | |
1599 | /// constraint is always true, and generates an error if it is never true. | |
1600 | /// The `trivial_bounds` feature changes this to be a warning in both | |
1601 | /// cases, giving macros more freedom and flexibility to generate code, | |
1602 | /// while still providing a signal when writing non-macro code that | |
1603 | /// something is amiss. | |
1604 | /// | |
1605 | /// See [RFC 2056] for more details. This feature is currently only | |
1606 | /// available on the nightly channel, see [tracking issue #48214]. | |
1607 | /// | |
1608 | /// [RFC 2056]: https://github.com/rust-lang/rfcs/blob/master/text/2056-allow-trivial-where-clause-constraints.md | |
1609 | /// [tracking issue #48214]: https://github.com/rust-lang/rust/issues/48214 | |
94b46f34 XL |
1610 | TRIVIAL_BOUNDS, |
1611 | Warn, | |
1612 | "these bounds don't depend on an type parameters" | |
1613 | } | |
1614 | ||
532ac7d7 XL |
1615 | declare_lint_pass!( |
1616 | /// Lint for trait and lifetime bounds that don't depend on type parameters | |
1617 | /// which either do nothing, or stop the item from being used. | |
1618 | TrivialConstraints => [TRIVIAL_BOUNDS] | |
1619 | ); | |
94b46f34 | 1620 | |
f035d41b XL |
1621 | impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { |
1622 | fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { | |
064997fb | 1623 | use rustc_middle::ty::visit::TypeVisitable; |
5869c6ff | 1624 | use rustc_middle::ty::PredicateKind::*; |
94b46f34 | 1625 | |
94b46f34 | 1626 | if cx.tcx.features().trivial_bounds { |
6a06907d | 1627 | let predicates = cx.tcx.predicates_of(item.def_id); |
e74abb32 | 1628 | for &(predicate, span) in predicates.predicates { |
5869c6ff | 1629 | let predicate_kind_name = match predicate.kind().skip_binder() { |
c295e0f8 | 1630 | Trait(..) => "trait", |
94b46f34 | 1631 | TypeOutlives(..) | |
c295e0f8 | 1632 | RegionOutlives(..) => "lifetime", |
94b46f34 XL |
1633 | |
1634 | // Ignore projections, as they can only be global | |
1635 | // if the trait bound is global | |
1636 | Projection(..) | | |
1637 | // Ignore bounds that a user can't type | |
1638 | WellFormed(..) | | |
1639 | ObjectSafe(..) | | |
1640 | ClosureKind(..) | | |
1641 | Subtype(..) | | |
94222f64 | 1642 | Coerce(..) | |
f9f354fc | 1643 | ConstEvaluatable(..) | |
1b1a35ee XL |
1644 | ConstEquate(..) | |
1645 | TypeWellFormedFromEnv(..) => continue, | |
94b46f34 | 1646 | }; |
5099ac24 | 1647 | if predicate.is_global() { |
74b04a01 | 1648 | cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| { |
064997fb FG |
1649 | lint.build(fluent::lint::builtin_trivial_bounds) |
1650 | .set_arg("predicate_kind_name", predicate_kind_name) | |
1651 | .set_arg("predicate", predicate) | |
1652 | .emit(); | |
74b04a01 | 1653 | }); |
94b46f34 XL |
1654 | } |
1655 | } | |
1656 | } | |
1657 | } | |
1658 | } | |
1659 | ||
532ac7d7 XL |
1660 | declare_lint_pass!( |
1661 | /// Does nothing as a lint pass, but registers some `Lint`s | |
1662 | /// which are used by other parts of the compiler. | |
1663 | SoftLints => [ | |
1664 | WHILE_TRUE, | |
1665 | BOX_POINTERS, | |
1666 | NON_SHORTHAND_FIELD_PATTERNS, | |
1667 | UNSAFE_CODE, | |
1668 | MISSING_DOCS, | |
1669 | MISSING_COPY_IMPLEMENTATIONS, | |
1670 | MISSING_DEBUG_IMPLEMENTATIONS, | |
1671 | ANONYMOUS_PARAMETERS, | |
1672 | UNUSED_DOC_COMMENTS, | |
532ac7d7 XL |
1673 | NO_MANGLE_CONST_ITEMS, |
1674 | NO_MANGLE_GENERIC_ITEMS, | |
1675 | MUTABLE_TRANSMUTES, | |
1676 | UNSTABLE_FEATURES, | |
532ac7d7 XL |
1677 | UNREACHABLE_PUB, |
1678 | TYPE_ALIAS_BOUNDS, | |
1679 | TRIVIAL_BOUNDS | |
1680 | ] | |
1681 | ); | |
8faf50e0 XL |
1682 | |
1683 | declare_lint! { | |
1b1a35ee XL |
1684 | /// The `ellipsis_inclusive_range_patterns` lint detects the [`...` range |
1685 | /// pattern], which is deprecated. | |
1686 | /// | |
1687 | /// [`...` range pattern]: https://doc.rust-lang.org/reference/patterns.html#range-patterns | |
1688 | /// | |
1689 | /// ### Example | |
1690 | /// | |
c295e0f8 | 1691 | /// ```rust,edition2018 |
1b1a35ee XL |
1692 | /// let x = 123; |
1693 | /// match x { | |
1694 | /// 0...100 => {} | |
1695 | /// _ => {} | |
1696 | /// } | |
1697 | /// ``` | |
1698 | /// | |
1699 | /// {{produces}} | |
1700 | /// | |
1701 | /// ### Explanation | |
1702 | /// | |
1703 | /// The `...` range pattern syntax was changed to `..=` to avoid potential | |
1704 | /// confusion with the [`..` range expression]. Use the new form instead. | |
1705 | /// | |
1706 | /// [`..` range expression]: https://doc.rust-lang.org/reference/expressions/range-expr.html | |
8faf50e0 | 1707 | pub ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, |
dc9dc135 | 1708 | Warn, |
17df50a5 XL |
1709 | "`...` range patterns are deprecated", |
1710 | @future_incompatible = FutureIncompatibleInfo { | |
94222f64 | 1711 | reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>", |
136023e0 | 1712 | reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021), |
17df50a5 | 1713 | }; |
8faf50e0 XL |
1714 | } |
1715 | ||
48663c56 XL |
1716 | #[derive(Default)] |
1717 | pub struct EllipsisInclusiveRangePatterns { | |
1718 | /// If `Some(_)`, suppress all subsequent pattern | |
1719 | /// warnings for better diagnostics. | |
1720 | node_id: Option<ast::NodeId>, | |
1721 | } | |
1722 | ||
1723 | impl_lint_pass!(EllipsisInclusiveRangePatterns => [ELLIPSIS_INCLUSIVE_RANGE_PATTERNS]); | |
8faf50e0 XL |
1724 | |
1725 | impl EarlyLintPass for EllipsisInclusiveRangePatterns { | |
48663c56 XL |
1726 | fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &ast::Pat) { |
1727 | if self.node_id.is_some() { | |
1728 | // Don't recursively warn about patterns inside range endpoints. | |
dfeec247 | 1729 | return; |
48663c56 XL |
1730 | } |
1731 | ||
3dfed10e | 1732 | use self::ast::{PatKind, RangeSyntax::DotDotDot}; |
13cf67c4 XL |
1733 | |
1734 | /// If `pat` is a `...` pattern, return the start and end of the range, as well as the span | |
1735 | /// corresponding to the ellipsis. | |
dfeec247 | 1736 | fn matches_ellipsis_pat(pat: &ast::Pat) -> Option<(Option<&Expr>, &Expr, Span)> { |
e74abb32 | 1737 | match &pat.kind { |
dfeec247 XL |
1738 | PatKind::Range( |
1739 | a, | |
1740 | Some(b), | |
1741 | Spanned { span, node: RangeEnd::Included(DotDotDot) }, | |
1742 | ) => Some((a.as_deref(), b, *span)), | |
13cf67c4 XL |
1743 | _ => None, |
1744 | } | |
1745 | } | |
8faf50e0 | 1746 | |
e74abb32 | 1747 | let (parenthesise, endpoints) = match &pat.kind { |
13cf67c4 XL |
1748 | PatKind::Ref(subpat, _) => (true, matches_ellipsis_pat(&subpat)), |
1749 | _ => (false, matches_ellipsis_pat(pat)), | |
1750 | }; | |
1751 | ||
1752 | if let Some((start, end, join)) = endpoints { | |
064997fb FG |
1753 | let msg = fluent::lint::builtin_ellipsis_inclusive_range_patterns; |
1754 | let suggestion = fluent::lint::suggestion; | |
13cf67c4 | 1755 | if parenthesise { |
48663c56 | 1756 | self.node_id = Some(pat.id); |
17df50a5 XL |
1757 | let end = expr_to_string(&end); |
1758 | let replace = match start { | |
1759 | Some(start) => format!("&({}..={})", expr_to_string(&start), end), | |
1760 | None => format!("&(..={})", end), | |
1761 | }; | |
1762 | if join.edition() >= Edition::Edition2021 { | |
064997fb FG |
1763 | let mut err = cx.sess().struct_span_err_with_code( |
1764 | pat.span, | |
1765 | msg, | |
1766 | rustc_errors::error_code!(E0783), | |
1767 | ); | |
17df50a5 XL |
1768 | err.span_suggestion( |
1769 | pat.span, | |
1770 | suggestion, | |
1771 | replace, | |
1772 | Applicability::MachineApplicable, | |
1773 | ) | |
1774 | .emit(); | |
1775 | } else { | |
1776 | cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, |lint| { | |
1777 | lint.build(msg) | |
1778 | .span_suggestion( | |
1779 | pat.span, | |
1780 | suggestion, | |
1781 | replace, | |
1782 | Applicability::MachineApplicable, | |
1783 | ) | |
1784 | .emit(); | |
1785 | }); | |
1786 | } | |
13cf67c4 | 1787 | } else { |
923072b8 | 1788 | let replace = "..="; |
17df50a5 | 1789 | if join.edition() >= Edition::Edition2021 { |
064997fb FG |
1790 | let mut err = cx.sess().struct_span_err_with_code( |
1791 | pat.span, | |
1792 | msg, | |
1793 | rustc_errors::error_code!(E0783), | |
1794 | ); | |
17df50a5 XL |
1795 | err.span_suggestion_short( |
1796 | join, | |
1797 | suggestion, | |
1798 | replace, | |
1799 | Applicability::MachineApplicable, | |
1800 | ) | |
1801 | .emit(); | |
1802 | } else { | |
1803 | cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, |lint| { | |
1804 | lint.build(msg) | |
1805 | .span_suggestion_short( | |
1806 | join, | |
1807 | suggestion, | |
1808 | replace, | |
1809 | Applicability::MachineApplicable, | |
1810 | ) | |
1811 | .emit(); | |
1812 | }); | |
1813 | } | |
13cf67c4 | 1814 | }; |
8faf50e0 XL |
1815 | } |
1816 | } | |
48663c56 XL |
1817 | |
1818 | fn check_pat_post(&mut self, _cx: &EarlyContext<'_>, pat: &ast::Pat) { | |
1819 | if let Some(node_id) = self.node_id { | |
1820 | if pat.id == node_id { | |
1821 | self.node_id = None | |
1822 | } | |
1823 | } | |
1824 | } | |
8faf50e0 XL |
1825 | } |
1826 | ||
1827 | declare_lint! { | |
1b1a35ee XL |
1828 | /// The `unnameable_test_items` lint detects [`#[test]`][test] functions |
1829 | /// that are not able to be run by the test harness because they are in a | |
1830 | /// position where they are not nameable. | |
1831 | /// | |
1832 | /// [test]: https://doc.rust-lang.org/reference/attributes/testing.html#the-test-attribute | |
1833 | /// | |
1834 | /// ### Example | |
1835 | /// | |
1836 | /// ```rust,test | |
1837 | /// fn main() { | |
1838 | /// #[test] | |
1839 | /// fn foo() { | |
1840 | /// // This test will not fail because it does not run. | |
1841 | /// assert_eq!(1, 2); | |
1842 | /// } | |
1843 | /// } | |
1844 | /// ``` | |
1845 | /// | |
1846 | /// {{produces}} | |
1847 | /// | |
1848 | /// ### Explanation | |
1849 | /// | |
1850 | /// In order for the test harness to run a test, the test function must be | |
1851 | /// located in a position where it can be accessed from the crate root. | |
1852 | /// This generally means it must be defined in a module, and not anywhere | |
1853 | /// else such as inside another function. The compiler previously allowed | |
1854 | /// this without an error, so a lint was added as an alert that a test is | |
1855 | /// not being used. Whether or not this should be allowed has not yet been | |
1856 | /// decided, see [RFC 2471] and [issue #36629]. | |
1857 | /// | |
1858 | /// [RFC 2471]: https://github.com/rust-lang/rfcs/pull/2471#issuecomment-397414443 | |
1859 | /// [issue #36629]: https://github.com/rust-lang/rust/issues/36629 | |
b7449926 | 1860 | UNNAMEABLE_TEST_ITEMS, |
8faf50e0 | 1861 | Warn, |
416331ca | 1862 | "detects an item that cannot be named being marked as `#[test_case]`", |
e74abb32 | 1863 | report_in_external_macro |
8faf50e0 XL |
1864 | } |
1865 | ||
b7449926 | 1866 | pub struct UnnameableTestItems { |
6a06907d | 1867 | boundary: Option<LocalDefId>, // Id of the item under which things are not nameable |
b7449926 XL |
1868 | items_nameable: bool, |
1869 | } | |
8faf50e0 | 1870 | |
532ac7d7 XL |
1871 | impl_lint_pass!(UnnameableTestItems => [UNNAMEABLE_TEST_ITEMS]); |
1872 | ||
b7449926 XL |
1873 | impl UnnameableTestItems { |
1874 | pub fn new() -> Self { | |
ba9703b0 | 1875 | Self { boundary: None, items_nameable: true } |
b7449926 XL |
1876 | } |
1877 | } | |
1878 | ||
f035d41b XL |
1879 | impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems { |
1880 | fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { | |
b7449926 | 1881 | if self.items_nameable { |
dfeec247 XL |
1882 | if let hir::ItemKind::Mod(..) = it.kind { |
1883 | } else { | |
b7449926 | 1884 | self.items_nameable = false; |
6a06907d | 1885 | self.boundary = Some(it.def_id); |
8faf50e0 | 1886 | } |
b7449926 XL |
1887 | return; |
1888 | } | |
1889 | ||
6a06907d XL |
1890 | let attrs = cx.tcx.hir().attrs(it.hir_id()); |
1891 | if let Some(attr) = cx.sess().find_by_name(attrs, sym::rustc_test_marker) { | |
74b04a01 | 1892 | cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, |lint| { |
064997fb | 1893 | lint.build(fluent::lint::builtin_unnameable_test_items).emit(); |
74b04a01 | 1894 | }); |
b7449926 XL |
1895 | } |
1896 | } | |
1897 | ||
f035d41b | 1898 | fn check_item_post(&mut self, _cx: &LateContext<'_>, it: &hir::Item<'_>) { |
6a06907d | 1899 | if !self.items_nameable && self.boundary == Some(it.def_id) { |
b7449926 XL |
1900 | self.items_nameable = true; |
1901 | } | |
8faf50e0 XL |
1902 | } |
1903 | } | |
1904 | ||
1905 | declare_lint! { | |
1b1a35ee XL |
1906 | /// The `keyword_idents` lint detects edition keywords being used as an |
1907 | /// identifier. | |
1908 | /// | |
1909 | /// ### Example | |
1910 | /// | |
1911 | /// ```rust,edition2015,compile_fail | |
1912 | /// #![deny(keyword_idents)] | |
1913 | /// // edition 2015 | |
1914 | /// fn dyn() {} | |
1915 | /// ``` | |
1916 | /// | |
1917 | /// {{produces}} | |
1918 | /// | |
1919 | /// ### Explanation | |
1920 | /// | |
1921 | /// Rust [editions] allow the language to evolve without breaking | |
1922 | /// backwards compatibility. This lint catches code that uses new keywords | |
1923 | /// that are added to the language that are used as identifiers (such as a | |
1924 | /// variable name, function name, etc.). If you switch the compiler to a | |
1925 | /// new edition without updating the code, then it will fail to compile if | |
1926 | /// you are using a new keyword as an identifier. | |
1927 | /// | |
1928 | /// You can manually change the identifiers to a non-keyword, or use a | |
1929 | /// [raw identifier], for example `r#dyn`, to transition to a new edition. | |
1930 | /// | |
1931 | /// This lint solves the problem automatically. It is "allow" by default | |
1932 | /// because the code is perfectly valid in older editions. The [`cargo | |
1933 | /// fix`] tool with the `--edition` flag will switch this lint to "warn" | |
1934 | /// and automatically apply the suggested fix from the compiler (which is | |
1935 | /// to use a raw identifier). This provides a completely automated way to | |
1936 | /// update old code for a new edition. | |
1937 | /// | |
1938 | /// [editions]: https://doc.rust-lang.org/edition-guide/ | |
1939 | /// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html | |
1940 | /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html | |
b7449926 | 1941 | pub KEYWORD_IDENTS, |
8faf50e0 | 1942 | Allow, |
e74abb32 XL |
1943 | "detects edition keywords being used as an identifier", |
1944 | @future_incompatible = FutureIncompatibleInfo { | |
1945 | reference: "issue #49716 <https://github.com/rust-lang/rust/issues/49716>", | |
136023e0 | 1946 | reason: FutureIncompatibilityReason::EditionError(Edition::Edition2018), |
e74abb32 | 1947 | }; |
8faf50e0 XL |
1948 | } |
1949 | ||
532ac7d7 XL |
1950 | declare_lint_pass!( |
1951 | /// Check for uses of edition keywords used as an identifier. | |
1952 | KeywordIdents => [KEYWORD_IDENTS] | |
1953 | ); | |
8faf50e0 | 1954 | |
532ac7d7 | 1955 | struct UnderMacro(bool); |
8faf50e0 | 1956 | |
b7449926 | 1957 | impl KeywordIdents { |
9fa01778 | 1958 | fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: TokenStream) { |
8faf50e0 XL |
1959 | for tt in tokens.into_trees() { |
1960 | match tt { | |
dc9dc135 | 1961 | // Only report non-raw idents. |
064997fb | 1962 | TokenTree::Token(token, _) => { |
dfeec247 XL |
1963 | if let Some((ident, false)) = token.ident() { |
1964 | self.check_ident_token(cx, UnderMacro(true), ident); | |
1965 | } | |
8faf50e0 | 1966 | } |
dfeec247 | 1967 | TokenTree::Delimited(_, _, tts) => self.check_tokens(cx, tts), |
8faf50e0 XL |
1968 | } |
1969 | } | |
1970 | } | |
8faf50e0 | 1971 | |
dfeec247 XL |
1972 | fn check_ident_token( |
1973 | &mut self, | |
1974 | cx: &EarlyContext<'_>, | |
1975 | UnderMacro(under_macro): UnderMacro, | |
f9f354fc | 1976 | ident: Ident, |
dfeec247 | 1977 | ) { |
5099ac24 | 1978 | let next_edition = match cx.sess().edition() { |
b7449926 | 1979 | Edition::Edition2015 => { |
dc9dc135 XL |
1980 | match ident.name { |
1981 | kw::Async | kw::Await | kw::Try => Edition::Edition2018, | |
532ac7d7 XL |
1982 | |
1983 | // rust-lang/rust#56327: Conservatively do not | |
1984 | // attempt to report occurrences of `dyn` within | |
1985 | // macro definitions or invocations, because `dyn` | |
1986 | // can legitimately occur as a contextual keyword | |
1987 | // in 2015 code denoting its 2018 meaning, and we | |
1988 | // do not want rustfix to inject bugs into working | |
1989 | // code by rewriting such occurrences. | |
1990 | // | |
1991 | // But if we see `dyn` outside of a macro, we know | |
1992 | // its precise role in the parsed AST and thus are | |
1993 | // assured this is truly an attempt to use it as | |
1994 | // an identifier. | |
dc9dc135 | 1995 | kw::Dyn if !under_macro => Edition::Edition2018, |
532ac7d7 | 1996 | |
b7449926 XL |
1997 | _ => return, |
1998 | } | |
1999 | } | |
2000 | ||
0731742a | 2001 | // There are no new keywords yet for the 2018 edition and beyond. |
48663c56 | 2002 | _ => return, |
b7449926 XL |
2003 | }; |
2004 | ||
dc9dc135 | 2005 | // Don't lint `r#foo`. |
5099ac24 | 2006 | if cx.sess().parse_sess.raw_identifier_spans.borrow().contains(&ident.span) { |
b7449926 | 2007 | return; |
8faf50e0 | 2008 | } |
b7449926 | 2009 | |
74b04a01 | 2010 | cx.struct_span_lint(KEYWORD_IDENTS, ident.span, |lint| { |
064997fb FG |
2011 | lint.build(fluent::lint::builtin_keyword_idents) |
2012 | .set_arg("kw", ident.clone()) | |
2013 | .set_arg("next", next_edition) | |
74b04a01 XL |
2014 | .span_suggestion( |
2015 | ident.span, | |
064997fb | 2016 | fluent::lint::suggestion, |
74b04a01 XL |
2017 | format!("r#{}", ident), |
2018 | Applicability::MachineApplicable, | |
2019 | ) | |
5e7ed085 | 2020 | .emit(); |
74b04a01 | 2021 | }); |
8faf50e0 XL |
2022 | } |
2023 | } | |
0bf4aa26 | 2024 | |
532ac7d7 XL |
2025 | impl EarlyLintPass for KeywordIdents { |
2026 | fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef, _id: ast::NodeId) { | |
60c5eb7d | 2027 | self.check_tokens(cx, mac_def.body.inner_tokens()); |
9fa01778 | 2028 | } |
ba9703b0 | 2029 | fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { |
60c5eb7d | 2030 | self.check_tokens(cx, mac.args.inner_tokens()); |
532ac7d7 | 2031 | } |
f9f354fc | 2032 | fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) { |
532ac7d7 | 2033 | self.check_ident_token(cx, UnderMacro(false), ident); |
0bf4aa26 XL |
2034 | } |
2035 | } | |
2036 | ||
532ac7d7 XL |
2037 | declare_lint_pass!(ExplicitOutlivesRequirements => [EXPLICIT_OUTLIVES_REQUIREMENTS]); |
2038 | ||
0bf4aa26 | 2039 | impl ExplicitOutlivesRequirements { |
dc9dc135 | 2040 | fn lifetimes_outliving_lifetime<'tcx>( |
e74abb32 | 2041 | inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)], |
dc9dc135 XL |
2042 | index: u32, |
2043 | ) -> Vec<ty::Region<'tcx>> { | |
dfeec247 XL |
2044 | inferred_outlives |
2045 | .iter() | |
5869c6ff | 2046 | .filter_map(|(pred, _)| match pred.kind().skip_binder() { |
5099ac24 | 2047 | ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a { |
3dfed10e XL |
2048 | ty::ReEarlyBound(ebr) if ebr.index == index => Some(b), |
2049 | _ => None, | |
2050 | }, | |
dfeec247 XL |
2051 | _ => None, |
2052 | }) | |
2053 | .collect() | |
dc9dc135 | 2054 | } |
0bf4aa26 | 2055 | |
dc9dc135 | 2056 | fn lifetimes_outliving_type<'tcx>( |
e74abb32 | 2057 | inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)], |
dc9dc135 XL |
2058 | index: u32, |
2059 | ) -> Vec<ty::Region<'tcx>> { | |
dfeec247 XL |
2060 | inferred_outlives |
2061 | .iter() | |
5869c6ff XL |
2062 | .filter_map(|(pred, _)| match pred.kind().skip_binder() { |
2063 | ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => { | |
3dfed10e | 2064 | a.is_param(index).then_some(b) |
0bf4aa26 | 2065 | } |
dfeec247 XL |
2066 | _ => None, |
2067 | }) | |
2068 | .collect() | |
dc9dc135 XL |
2069 | } |
2070 | ||
dc9dc135 XL |
2071 | fn collect_outlives_bound_spans<'tcx>( |
2072 | &self, | |
2073 | tcx: TyCtxt<'tcx>, | |
dfeec247 | 2074 | bounds: &hir::GenericBounds<'_>, |
dc9dc135 | 2075 | inferred_outlives: &[ty::Region<'tcx>], |
dc9dc135 | 2076 | ) -> Vec<(usize, Span)> { |
ba9703b0 | 2077 | use rustc_middle::middle::resolve_lifetime::Region; |
dc9dc135 XL |
2078 | |
2079 | bounds | |
2080 | .iter() | |
2081 | .enumerate() | |
2082 | .filter_map(|(i, bound)| { | |
2083 | if let hir::GenericBound::Outlives(lifetime) = bound { | |
2084 | let is_inferred = match tcx.named_region(lifetime.hir_id) { | |
dfeec247 | 2085 | Some(Region::EarlyBound(index, ..)) => inferred_outlives.iter().any(|r| { |
5099ac24 | 2086 | if let ty::ReEarlyBound(ebr) = **r { ebr.index == index } else { false } |
dfeec247 | 2087 | }), |
dc9dc135 XL |
2088 | _ => false, |
2089 | }; | |
60c5eb7d | 2090 | is_inferred.then_some((i, bound.span())) |
dc9dc135 XL |
2091 | } else { |
2092 | None | |
2093 | } | |
2094 | }) | |
04454e1e | 2095 | .filter(|(_, span)| !in_external_macro(tcx.sess, *span)) |
dc9dc135 | 2096 | .collect() |
0bf4aa26 XL |
2097 | } |
2098 | ||
2099 | fn consolidate_outlives_bound_spans( | |
2100 | &self, | |
2101 | lo: Span, | |
dfeec247 XL |
2102 | bounds: &hir::GenericBounds<'_>, |
2103 | bound_spans: Vec<(usize, Span)>, | |
0bf4aa26 XL |
2104 | ) -> Vec<Span> { |
2105 | if bounds.is_empty() { | |
2106 | return Vec::new(); | |
2107 | } | |
2108 | if bound_spans.len() == bounds.len() { | |
dfeec247 | 2109 | let (_, last_bound_span) = bound_spans[bound_spans.len() - 1]; |
0bf4aa26 XL |
2110 | // If all bounds are inferable, we want to delete the colon, so |
2111 | // start from just after the parameter (span passed as argument) | |
2112 | vec![lo.to(last_bound_span)] | |
2113 | } else { | |
2114 | let mut merged = Vec::new(); | |
2115 | let mut last_merged_i = None; | |
2116 | ||
2117 | let mut from_start = true; | |
2118 | for (i, bound_span) in bound_spans { | |
2119 | match last_merged_i { | |
dc9dc135 | 2120 | // If the first bound is inferable, our span should also eat the leading `+`. |
0bf4aa26 XL |
2121 | None if i == 0 => { |
2122 | merged.push(bound_span.to(bounds[1].span().shrink_to_lo())); | |
2123 | last_merged_i = Some(0); | |
dfeec247 | 2124 | } |
0bf4aa26 | 2125 | // If consecutive bounds are inferable, merge their spans |
dfeec247 | 2126 | Some(h) if i == h + 1 => { |
0bf4aa26 XL |
2127 | if let Some(tail) = merged.last_mut() { |
2128 | // Also eat the trailing `+` if the first | |
2129 | // more-than-one bound is inferable | |
2130 | let to_span = if from_start && i < bounds.len() { | |
dfeec247 | 2131 | bounds[i + 1].span().shrink_to_lo() |
0bf4aa26 XL |
2132 | } else { |
2133 | bound_span | |
2134 | }; | |
2135 | *tail = tail.to(to_span); | |
2136 | last_merged_i = Some(i); | |
2137 | } else { | |
2138 | bug!("another bound-span visited earlier"); | |
2139 | } | |
dfeec247 | 2140 | } |
0bf4aa26 XL |
2141 | _ => { |
2142 | // When we find a non-inferable bound, subsequent inferable bounds | |
2143 | // won't be consecutive from the start (and we'll eat the leading | |
2144 | // `+` rather than the trailing one) | |
2145 | from_start = false; | |
dfeec247 | 2146 | merged.push(bounds[i - 1].span().shrink_to_hi().to(bound_span)); |
0bf4aa26 XL |
2147 | last_merged_i = Some(i); |
2148 | } | |
2149 | } | |
2150 | } | |
2151 | merged | |
2152 | } | |
2153 | } | |
2154 | } | |
2155 | ||
f035d41b XL |
2156 | impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { |
2157 | fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { | |
ba9703b0 | 2158 | use rustc_middle::middle::resolve_lifetime::Region; |
dc9dc135 | 2159 | |
6a06907d | 2160 | let def_id = item.def_id; |
dc9dc135 | 2161 | if let hir::ItemKind::Struct(_, ref hir_generics) |
dfeec247 XL |
2162 | | hir::ItemKind::Enum(_, ref hir_generics) |
2163 | | hir::ItemKind::Union(_, ref hir_generics) = item.kind | |
dc9dc135 XL |
2164 | { |
2165 | let inferred_outlives = cx.tcx.inferred_outlives_of(def_id); | |
2166 | if inferred_outlives.is_empty() { | |
2167 | return; | |
2168 | } | |
2169 | ||
2170 | let ty_generics = cx.tcx.generics_of(def_id); | |
2171 | ||
0bf4aa26 XL |
2172 | let mut bound_count = 0; |
2173 | let mut lint_spans = Vec::new(); | |
0bf4aa26 XL |
2174 | let mut where_lint_spans = Vec::new(); |
2175 | let mut dropped_predicate_count = 0; | |
04454e1e FG |
2176 | let num_predicates = hir_generics.predicates.len(); |
2177 | for (i, where_predicate) in hir_generics.predicates.iter().enumerate() { | |
2178 | let (relevant_lifetimes, bounds, span, in_where_clause) = match where_predicate { | |
dc9dc135 | 2179 | hir::WherePredicate::RegionPredicate(predicate) => { |
dfeec247 XL |
2180 | if let Some(Region::EarlyBound(index, ..)) = |
2181 | cx.tcx.named_region(predicate.lifetime.hir_id) | |
dc9dc135 XL |
2182 | { |
2183 | ( | |
2184 | Self::lifetimes_outliving_lifetime(inferred_outlives, index), | |
0bf4aa26 | 2185 | &predicate.bounds, |
dc9dc135 | 2186 | predicate.span, |
04454e1e | 2187 | predicate.in_where_clause, |
0bf4aa26 | 2188 | ) |
dc9dc135 XL |
2189 | } else { |
2190 | continue; | |
2191 | } | |
0bf4aa26 | 2192 | } |
dc9dc135 XL |
2193 | hir::WherePredicate::BoundPredicate(predicate) => { |
2194 | // FIXME we can also infer bounds on associated types, | |
2195 | // and should check for them here. | |
e74abb32 | 2196 | match predicate.bounded_ty.kind { |
dfeec247 | 2197 | hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => { |
a2a8927a XL |
2198 | let Res::Def(DefKind::TyParam, def_id) = path.res else { |
2199 | continue | |
2200 | }; | |
2201 | let index = ty_generics.param_def_id_to_index[&def_id]; | |
2202 | ( | |
2203 | Self::lifetimes_outliving_type(inferred_outlives, index), | |
2204 | &predicate.bounds, | |
2205 | predicate.span, | |
04454e1e | 2206 | predicate.origin == PredicateOrigin::WhereClause, |
a2a8927a | 2207 | ) |
dfeec247 XL |
2208 | } |
2209 | _ => { | |
2210 | continue; | |
2211 | } | |
dc9dc135 XL |
2212 | } |
2213 | } | |
2214 | _ => continue, | |
2215 | }; | |
2216 | if relevant_lifetimes.is_empty() { | |
2217 | continue; | |
2218 | } | |
2219 | ||
923072b8 FG |
2220 | let bound_spans = |
2221 | self.collect_outlives_bound_spans(cx.tcx, bounds, &relevant_lifetimes); | |
dc9dc135 XL |
2222 | bound_count += bound_spans.len(); |
2223 | ||
2224 | let drop_predicate = bound_spans.len() == bounds.len(); | |
2225 | if drop_predicate { | |
2226 | dropped_predicate_count += 1; | |
2227 | } | |
2228 | ||
04454e1e FG |
2229 | if drop_predicate && !in_where_clause { |
2230 | lint_spans.push(span); | |
2231 | } else if drop_predicate && i + 1 < num_predicates { | |
2232 | // If all the bounds on a predicate were inferable and there are | |
2233 | // further predicates, we want to eat the trailing comma. | |
2234 | let next_predicate_span = hir_generics.predicates[i + 1].span(); | |
dfeec247 | 2235 | where_lint_spans.push(span.to(next_predicate_span.shrink_to_lo())); |
dc9dc135 | 2236 | } else { |
dfeec247 XL |
2237 | where_lint_spans.extend(self.consolidate_outlives_bound_spans( |
2238 | span.shrink_to_lo(), | |
2239 | bounds, | |
2240 | bound_spans, | |
2241 | )); | |
0bf4aa26 XL |
2242 | } |
2243 | } | |
2244 | ||
2245 | // If all predicates are inferable, drop the entire clause | |
2246 | // (including the `where`) | |
923072b8 FG |
2247 | if hir_generics.has_where_clause_predicates && dropped_predicate_count == num_predicates |
2248 | { | |
2249 | let where_span = hir_generics.where_clause_span; | |
dc9dc135 XL |
2250 | // Extend the where clause back to the closing `>` of the |
2251 | // generics, except for tuple struct, which have the `where` | |
2252 | // after the fields of the struct. | |
dfeec247 XL |
2253 | let full_where_span = |
2254 | if let hir::ItemKind::Struct(hir::VariantData::Tuple(..), _) = item.kind { | |
2255 | where_span | |
2256 | } else { | |
2257 | hir_generics.span.shrink_to_hi().to(where_span) | |
2258 | }; | |
2259 | lint_spans.push(full_where_span); | |
0bf4aa26 XL |
2260 | } else { |
2261 | lint_spans.extend(where_lint_spans); | |
2262 | } | |
2263 | ||
2264 | if !lint_spans.is_empty() { | |
74b04a01 | 2265 | cx.struct_span_lint(EXPLICIT_OUTLIVES_REQUIREMENTS, lint_spans.clone(), |lint| { |
064997fb FG |
2266 | lint.build(fluent::lint::builtin_explicit_outlives) |
2267 | .set_arg("count", bound_count) | |
74b04a01 | 2268 | .multipart_suggestion( |
064997fb | 2269 | fluent::lint::suggestion, |
74b04a01 XL |
2270 | lint_spans |
2271 | .into_iter() | |
04454e1e | 2272 | .map(|span| (span, String::new())) |
74b04a01 XL |
2273 | .collect::<Vec<_>>(), |
2274 | Applicability::MachineApplicable, | |
2275 | ) | |
2276 | .emit(); | |
2277 | }); | |
0bf4aa26 | 2278 | } |
0bf4aa26 XL |
2279 | } |
2280 | } | |
0bf4aa26 | 2281 | } |
416331ca XL |
2282 | |
2283 | declare_lint! { | |
1b1a35ee XL |
2284 | /// The `incomplete_features` lint detects unstable features enabled with |
2285 | /// the [`feature` attribute] that may function improperly in some or all | |
2286 | /// cases. | |
2287 | /// | |
2288 | /// [`feature` attribute]: https://doc.rust-lang.org/nightly/unstable-book/ | |
2289 | /// | |
2290 | /// ### Example | |
2291 | /// | |
2292 | /// ```rust | |
94222f64 | 2293 | /// #![feature(generic_const_exprs)] |
1b1a35ee XL |
2294 | /// ``` |
2295 | /// | |
2296 | /// {{produces}} | |
2297 | /// | |
2298 | /// ### Explanation | |
2299 | /// | |
2300 | /// Although it is encouraged for people to experiment with unstable | |
2301 | /// features, some of them are known to be incomplete or faulty. This lint | |
2302 | /// is a signal that the feature has not yet been finished, and you may | |
2303 | /// experience problems with it. | |
416331ca XL |
2304 | pub INCOMPLETE_FEATURES, |
2305 | Warn, | |
2306 | "incomplete features that may function improperly in some or all cases" | |
2307 | } | |
2308 | ||
2309 | declare_lint_pass!( | |
cdc7bbd5 | 2310 | /// Check for used feature gates in `INCOMPLETE_FEATURES` in `rustc_feature/src/active.rs`. |
416331ca XL |
2311 | IncompleteFeatures => [INCOMPLETE_FEATURES] |
2312 | ); | |
2313 | ||
2314 | impl EarlyLintPass for IncompleteFeatures { | |
2315 | fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { | |
5099ac24 | 2316 | let features = cx.sess().features_untracked(); |
dfeec247 XL |
2317 | features |
2318 | .declared_lang_features | |
2319 | .iter() | |
2320 | .map(|(name, span, _)| (name, span)) | |
416331ca | 2321 | .chain(features.declared_lib_features.iter().map(|(name, span)| (name, span))) |
136023e0 | 2322 | .filter(|(&name, _)| features.incomplete(name)) |
f9f354fc | 2323 | .for_each(|(&name, &span)| { |
74b04a01 | 2324 | cx.struct_span_lint(INCOMPLETE_FEATURES, span, |lint| { |
064997fb FG |
2325 | let mut builder = lint.build(fluent::lint::builtin_incomplete_features); |
2326 | builder.set_arg("name", name); | |
f9f354fc | 2327 | if let Some(n) = rustc_feature::find_feature_issue(name, GateIssue::Language) { |
064997fb FG |
2328 | builder.set_arg("n", n); |
2329 | builder.note(fluent::lint::note); | |
f9f354fc | 2330 | } |
29967ef6 | 2331 | if HAS_MIN_FEATURES.contains(&name) { |
064997fb | 2332 | builder.help(fluent::lint::help); |
29967ef6 | 2333 | } |
f9f354fc | 2334 | builder.emit(); |
74b04a01 | 2335 | }) |
416331ca XL |
2336 | }); |
2337 | } | |
2338 | } | |
2339 | ||
5869c6ff | 2340 | const HAS_MIN_FEATURES: &[Symbol] = &[sym::specialization]; |
29967ef6 | 2341 | |
416331ca | 2342 | declare_lint! { |
1b1a35ee | 2343 | /// The `invalid_value` lint detects creating a value that is not valid, |
17df50a5 | 2344 | /// such as a null reference. |
1b1a35ee XL |
2345 | /// |
2346 | /// ### Example | |
2347 | /// | |
2348 | /// ```rust,no_run | |
2349 | /// # #![allow(unused)] | |
2350 | /// unsafe { | |
2351 | /// let x: &'static i32 = std::mem::zeroed(); | |
2352 | /// } | |
2353 | /// ``` | |
2354 | /// | |
2355 | /// {{produces}} | |
2356 | /// | |
2357 | /// ### Explanation | |
2358 | /// | |
2359 | /// In some situations the compiler can detect that the code is creating | |
2360 | /// an invalid value, which should be avoided. | |
2361 | /// | |
2362 | /// In particular, this lint will check for improper use of | |
2363 | /// [`mem::zeroed`], [`mem::uninitialized`], [`mem::transmute`], and | |
2364 | /// [`MaybeUninit::assume_init`] that can cause [undefined behavior]. The | |
2365 | /// lint should provide extra information to indicate what the problem is | |
2366 | /// and a possible solution. | |
2367 | /// | |
2368 | /// [`mem::zeroed`]: https://doc.rust-lang.org/std/mem/fn.zeroed.html | |
2369 | /// [`mem::uninitialized`]: https://doc.rust-lang.org/std/mem/fn.uninitialized.html | |
2370 | /// [`mem::transmute`]: https://doc.rust-lang.org/std/mem/fn.transmute.html | |
2371 | /// [`MaybeUninit::assume_init`]: https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#method.assume_init | |
2372 | /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html | |
416331ca XL |
2373 | pub INVALID_VALUE, |
2374 | Warn, | |
17df50a5 | 2375 | "an invalid value is being created (such as a null reference)" |
416331ca XL |
2376 | } |
2377 | ||
2378 | declare_lint_pass!(InvalidValue => [INVALID_VALUE]); | |
2379 | ||
f035d41b XL |
2380 | impl<'tcx> LateLintPass<'tcx> for InvalidValue { |
2381 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) { | |
e1599b0c | 2382 | #[derive(Debug, Copy, Clone, PartialEq)] |
dfeec247 XL |
2383 | enum InitKind { |
2384 | Zeroed, | |
2385 | Uninit, | |
fc512014 | 2386 | } |
416331ca XL |
2387 | |
2388 | /// Information about why a type cannot be initialized this way. | |
2389 | /// Contains an error message and optionally a span to point at. | |
2390 | type InitError = (String, Option<Span>); | |
2391 | ||
e1599b0c | 2392 | /// Test if this constant is all-0. |
dfeec247 | 2393 | fn is_zero(expr: &hir::Expr<'_>) -> bool { |
e1599b0c | 2394 | use hir::ExprKind::*; |
3dfed10e | 2395 | use rustc_ast::LitKind::*; |
e74abb32 | 2396 | match &expr.kind { |
dfeec247 | 2397 | Lit(lit) => { |
e1599b0c XL |
2398 | if let Int(i, _) = lit.node { |
2399 | i == 0 | |
2400 | } else { | |
2401 | false | |
dfeec247 XL |
2402 | } |
2403 | } | |
2404 | Tup(tup) => tup.iter().all(is_zero), | |
2405 | _ => false, | |
e1599b0c XL |
2406 | } |
2407 | } | |
2408 | ||
2409 | /// Determine if this expression is a "dangerous initialization". | |
f035d41b | 2410 | fn is_dangerous_init(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<InitKind> { |
e74abb32 | 2411 | if let hir::ExprKind::Call(ref path_expr, ref args) = expr.kind { |
60c5eb7d | 2412 | // Find calls to `mem::{uninitialized,zeroed}` methods. |
e74abb32 | 2413 | if let hir::ExprKind::Path(ref qpath) = path_expr.kind { |
f035d41b | 2414 | let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; |
c295e0f8 XL |
2415 | match cx.tcx.get_diagnostic_name(def_id) { |
2416 | Some(sym::mem_zeroed) => return Some(InitKind::Zeroed), | |
2417 | Some(sym::mem_uninitialized) => return Some(InitKind::Uninit), | |
2418 | Some(sym::transmute) if is_zero(&args[0]) => return Some(InitKind::Zeroed), | |
2419 | _ => {} | |
e1599b0c | 2420 | } |
60c5eb7d | 2421 | } |
5099ac24 | 2422 | } else if let hir::ExprKind::MethodCall(_, ref args, _) = expr.kind { |
60c5eb7d | 2423 | // Find problematic calls to `MaybeUninit::assume_init`. |
3dfed10e | 2424 | let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; |
60c5eb7d XL |
2425 | if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) { |
2426 | // This is a call to *some* method named `assume_init`. | |
2427 | // See if the `self` parameter is one of the dangerous constructors. | |
2428 | if let hir::ExprKind::Call(ref path_expr, _) = args[0].kind { | |
2429 | if let hir::ExprKind::Path(ref qpath) = path_expr.kind { | |
f035d41b | 2430 | let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; |
c295e0f8 XL |
2431 | match cx.tcx.get_diagnostic_name(def_id) { |
2432 | Some(sym::maybe_uninit_zeroed) => return Some(InitKind::Zeroed), | |
2433 | Some(sym::maybe_uninit_uninit) => return Some(InitKind::Uninit), | |
2434 | _ => {} | |
60c5eb7d XL |
2435 | } |
2436 | } | |
2437 | } | |
e1599b0c XL |
2438 | } |
2439 | } | |
2440 | ||
2441 | None | |
2442 | } | |
2443 | ||
3dfed10e XL |
2444 | /// Test if this enum has several actually "existing" variants. |
2445 | /// Zero-sized uninhabited variants do not always have a tag assigned and thus do not "exist". | |
5e7ed085 | 2446 | fn is_multi_variant<'tcx>(adt: ty::AdtDef<'tcx>) -> bool { |
3dfed10e | 2447 | // As an approximation, we only count dataless variants. Those are definitely inhabited. |
5e7ed085 | 2448 | let existing_variants = adt.variants().iter().filter(|v| v.fields.is_empty()).count(); |
3dfed10e XL |
2449 | existing_variants > 1 |
2450 | } | |
2451 | ||
416331ca XL |
2452 | /// Return `Some` only if we are sure this type does *not* |
2453 | /// allow zero initialization. | |
e1599b0c | 2454 | fn ty_find_init_error<'tcx>( |
5e7ed085 | 2455 | cx: &LateContext<'tcx>, |
e1599b0c XL |
2456 | ty: Ty<'tcx>, |
2457 | init: InitKind, | |
2458 | ) -> Option<InitError> { | |
923072b8 | 2459 | use rustc_type_ir::sty::TyKind::*; |
1b1a35ee | 2460 | match ty.kind() { |
416331ca | 2461 | // Primitive types that don't like 0 as a value. |
74b04a01 XL |
2462 | Ref(..) => Some(("references must be non-null".to_string(), None)), |
2463 | Adt(..) if ty.is_box() => Some(("`Box` must be non-null".to_string(), None)), | |
2464 | FnPtr(..) => Some(("function pointers must be non-null".to_string(), None)), | |
2465 | Never => Some(("the `!` type has no valid value".to_string(), None)), | |
1b1a35ee | 2466 | RawPtr(tm) if matches!(tm.ty.kind(), Dynamic(..)) => |
dfeec247 XL |
2467 | // raw ptr to dyn Trait |
2468 | { | |
74b04a01 | 2469 | Some(("the vtable of a wide raw pointer must be non-null".to_string(), None)) |
dfeec247 | 2470 | } |
e1599b0c | 2471 | // Primitive types with other constraints. |
dfeec247 | 2472 | Bool if init == InitKind::Uninit => { |
74b04a01 | 2473 | Some(("booleans must be either `true` or `false`".to_string(), None)) |
dfeec247 XL |
2474 | } |
2475 | Char if init == InitKind::Uninit => { | |
74b04a01 | 2476 | Some(("characters must be a valid Unicode codepoint".to_string(), None)) |
dfeec247 | 2477 | } |
e1599b0c | 2478 | // Recurse and checks for some compound types. |
416331ca | 2479 | Adt(adt_def, substs) if !adt_def.is_union() => { |
3dfed10e | 2480 | // First check if this ADT has a layout attribute (like `NonNull` and friends). |
e1599b0c | 2481 | use std::ops::Bound; |
5e7ed085 | 2482 | match cx.tcx.layout_scalar_valid_range(adt_def.did()) { |
e1599b0c XL |
2483 | // We exploit here that `layout_scalar_valid_range` will never |
2484 | // return `Bound::Excluded`. (And we have tests checking that we | |
2485 | // handle the attribute correctly.) | |
dfeec247 XL |
2486 | (Bound::Included(lo), _) if lo > 0 => { |
2487 | return Some((format!("`{}` must be non-null", ty), None)); | |
2488 | } | |
e1599b0c | 2489 | (Bound::Included(_), _) | (_, Bound::Included(_)) |
dfeec247 XL |
2490 | if init == InitKind::Uninit => |
2491 | { | |
e1599b0c | 2492 | return Some(( |
dfeec247 XL |
2493 | format!( |
2494 | "`{}` must be initialized inside its custom valid range", | |
2495 | ty, | |
2496 | ), | |
e1599b0c | 2497 | None, |
dfeec247 XL |
2498 | )); |
2499 | } | |
e1599b0c XL |
2500 | _ => {} |
2501 | } | |
2502 | // Now, recurse. | |
5e7ed085 | 2503 | match adt_def.variants().len() { |
74b04a01 | 2504 | 0 => Some(("enums with no variants have no valid value".to_string(), None)), |
416331ca XL |
2505 | 1 => { |
2506 | // Struct, or enum with exactly one variant. | |
2507 | // Proceed recursively, check all fields. | |
5e7ed085 | 2508 | let variant = &adt_def.variant(VariantIdx::from_u32(0)); |
416331ca | 2509 | variant.fields.iter().find_map(|field| { |
5e7ed085 | 2510 | ty_find_init_error(cx, field.ty(cx.tcx, substs), init).map( |
dfeec247 XL |
2511 | |(mut msg, span)| { |
2512 | if span.is_none() { | |
2513 | // Point to this field, should be helpful for figuring | |
2514 | // out where the source of the error is. | |
5e7ed085 | 2515 | let span = cx.tcx.def_span(field.did); |
dfeec247 XL |
2516 | write!( |
2517 | &mut msg, | |
2518 | " (in this {} field)", | |
2519 | adt_def.descr() | |
2520 | ) | |
2521 | .unwrap(); | |
2522 | (msg, Some(span)) | |
2523 | } else { | |
2524 | // Just forward. | |
2525 | (msg, span) | |
2526 | } | |
2527 | }, | |
2528 | ) | |
416331ca XL |
2529 | }) |
2530 | } | |
3dfed10e XL |
2531 | // Multi-variant enum. |
2532 | _ => { | |
5e7ed085 FG |
2533 | if init == InitKind::Uninit && is_multi_variant(*adt_def) { |
2534 | let span = cx.tcx.def_span(adt_def.did()); | |
3dfed10e XL |
2535 | Some(( |
2536 | "enums have to be initialized to a variant".to_string(), | |
2537 | Some(span), | |
2538 | )) | |
2539 | } else { | |
2540 | // In principle, for zero-initialization we could figure out which variant corresponds | |
2541 | // to tag 0, and check that... but for now we just accept all zero-initializations. | |
2542 | None | |
2543 | } | |
2544 | } | |
416331ca XL |
2545 | } |
2546 | } | |
2547 | Tuple(..) => { | |
2548 | // Proceed recursively, check all fields. | |
5e7ed085 FG |
2549 | ty.tuple_fields().iter().find_map(|field| ty_find_init_error(cx, field, init)) |
2550 | } | |
2551 | Array(ty, len) => { | |
2552 | if matches!(len.try_eval_usize(cx.tcx, cx.param_env), Some(v) if v > 0) { | |
2553 | // Array length known at array non-empty -- recurse. | |
2554 | ty_find_init_error(cx, *ty, init) | |
2555 | } else { | |
2556 | // Empty array or size unknown. | |
2557 | None | |
2558 | } | |
416331ca | 2559 | } |
416331ca XL |
2560 | // Conservative fallback. |
2561 | _ => None, | |
2562 | } | |
2563 | } | |
2564 | ||
e1599b0c XL |
2565 | if let Some(init) = is_dangerous_init(cx, expr) { |
2566 | // This conjures an instance of a type out of nothing, | |
2567 | // using zeroed or uninitialized memory. | |
2568 | // We are extremely conservative with what we warn about. | |
3dfed10e | 2569 | let conjured_ty = cx.typeck_results().expr_ty(expr); |
1b1a35ee | 2570 | if let Some((msg, span)) = |
5e7ed085 | 2571 | with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) |
1b1a35ee | 2572 | { |
064997fb | 2573 | // FIXME(davidtwco): make translatable |
74b04a01 XL |
2574 | cx.struct_span_lint(INVALID_VALUE, expr.span, |lint| { |
2575 | let mut err = lint.build(&format!( | |
e1599b0c XL |
2576 | "the type `{}` does not permit {}", |
2577 | conjured_ty, | |
2578 | match init { | |
2579 | InitKind::Zeroed => "zero-initialization", | |
2580 | InitKind::Uninit => "being left uninitialized", | |
2581 | }, | |
74b04a01 XL |
2582 | )); |
2583 | err.span_label(expr.span, "this code causes undefined behavior when executed"); | |
2584 | err.span_label( | |
2585 | expr.span, | |
2586 | "help: use `MaybeUninit<T>` instead, \ | |
2587 | and only call `assume_init` after initialization is done", | |
2588 | ); | |
2589 | if let Some(span) = span { | |
2590 | err.span_note(span, &msg); | |
2591 | } else { | |
2592 | err.note(&msg); | |
2593 | } | |
2594 | err.emit(); | |
2595 | }); | |
416331ca XL |
2596 | } |
2597 | } | |
2598 | } | |
2599 | } | |
f035d41b XL |
2600 | |
2601 | declare_lint! { | |
1b1a35ee XL |
2602 | /// The `clashing_extern_declarations` lint detects when an `extern fn` |
2603 | /// has been declared with the same name but different types. | |
2604 | /// | |
2605 | /// ### Example | |
2606 | /// | |
2607 | /// ```rust | |
2608 | /// mod m { | |
2609 | /// extern "C" { | |
2610 | /// fn foo(); | |
2611 | /// } | |
2612 | /// } | |
2613 | /// | |
2614 | /// extern "C" { | |
2615 | /// fn foo(_: u32); | |
2616 | /// } | |
2617 | /// ``` | |
2618 | /// | |
2619 | /// {{produces}} | |
2620 | /// | |
2621 | /// ### Explanation | |
2622 | /// | |
2623 | /// Because two symbols of the same name cannot be resolved to two | |
2624 | /// different functions at link time, and one function cannot possibly | |
2625 | /// have two types, a clashing extern declaration is almost certainly a | |
2626 | /// mistake. Check to make sure that the `extern` definitions are correct | |
2627 | /// and equivalent, and possibly consider unifying them in one location. | |
2628 | /// | |
2629 | /// This lint does not run between crates because a project may have | |
2630 | /// dependencies which both rely on the same extern function, but declare | |
2631 | /// it in a different (but valid) way. For example, they may both declare | |
2632 | /// an opaque type for one or more of the arguments (which would end up | |
2633 | /// distinct types), or use types that are valid conversions in the | |
2634 | /// language the `extern fn` is defined in. In these cases, the compiler | |
2635 | /// can't say that the clashing declaration is incorrect. | |
f035d41b | 2636 | pub CLASHING_EXTERN_DECLARATIONS, |
3dfed10e | 2637 | Warn, |
f035d41b XL |
2638 | "detects when an extern fn has been declared with the same name but different types" |
2639 | } | |
2640 | ||
2641 | pub struct ClashingExternDeclarations { | |
5869c6ff XL |
2642 | /// Map of function symbol name to the first-seen hir id for that symbol name.. If seen_decls |
2643 | /// contains an entry for key K, it means a symbol with name K has been seen by this lint and | |
2644 | /// the symbol should be reported as a clashing declaration. | |
2645 | // FIXME: Technically, we could just store a &'tcx str here without issue; however, the | |
2646 | // `impl_lint_pass` macro doesn't currently support lints parametric over a lifetime. | |
f035d41b XL |
2647 | seen_decls: FxHashMap<Symbol, HirId>, |
2648 | } | |
2649 | ||
2650 | /// Differentiate between whether the name for an extern decl came from the link_name attribute or | |
2651 | /// just from declaration itself. This is important because we don't want to report clashes on | |
2652 | /// symbol name if they don't actually clash because one or the other links against a symbol with a | |
2653 | /// different name. | |
2654 | enum SymbolName { | |
2655 | /// The name of the symbol + the span of the annotation which introduced the link name. | |
2656 | Link(Symbol, Span), | |
2657 | /// No link name, so just the name of the symbol. | |
2658 | Normal(Symbol), | |
2659 | } | |
2660 | ||
2661 | impl SymbolName { | |
2662 | fn get_name(&self) -> Symbol { | |
2663 | match self { | |
2664 | SymbolName::Link(s, _) | SymbolName::Normal(s) => *s, | |
2665 | } | |
2666 | } | |
2667 | } | |
2668 | ||
2669 | impl ClashingExternDeclarations { | |
923072b8 | 2670 | pub(crate) fn new() -> Self { |
f035d41b XL |
2671 | ClashingExternDeclarations { seen_decls: FxHashMap::default() } |
2672 | } | |
2673 | /// Insert a new foreign item into the seen set. If a symbol with the same name already exists | |
2674 | /// for the item, return its HirId without updating the set. | |
2675 | fn insert(&mut self, tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> Option<HirId> { | |
6a06907d | 2676 | let did = fi.def_id.to_def_id(); |
5869c6ff XL |
2677 | let instance = Instance::new(did, ty::List::identity_for_item(tcx, did)); |
2678 | let name = Symbol::intern(tcx.symbol_name(instance).name); | |
2679 | if let Some(&hir_id) = self.seen_decls.get(&name) { | |
f035d41b XL |
2680 | // Avoid updating the map with the new entry when we do find a collision. We want to |
2681 | // make sure we're always pointing to the first definition as the previous declaration. | |
2682 | // This lets us avoid emitting "knock-on" diagnostics. | |
5869c6ff | 2683 | Some(hir_id) |
f035d41b | 2684 | } else { |
6a06907d | 2685 | self.seen_decls.insert(name, fi.hir_id()) |
f035d41b XL |
2686 | } |
2687 | } | |
2688 | ||
2689 | /// Get the name of the symbol that's linked against for a given extern declaration. That is, | |
2690 | /// the name specified in a #[link_name = ...] attribute if one was specified, else, just the | |
2691 | /// symbol's name. | |
2692 | fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> SymbolName { | |
f035d41b | 2693 | if let Some((overridden_link_name, overridden_link_name_span)) = |
6a06907d | 2694 | tcx.codegen_fn_attrs(fi.def_id).link_name.map(|overridden_link_name| { |
f035d41b XL |
2695 | // FIXME: Instead of searching through the attributes again to get span |
2696 | // information, we could have codegen_fn_attrs also give span information back for | |
2697 | // where the attribute was defined. However, until this is found to be a | |
2698 | // bottleneck, this does just fine. | |
2699 | ( | |
2700 | overridden_link_name, | |
04454e1e | 2701 | tcx.get_attr(fi.def_id.to_def_id(), sym::link_name).unwrap().span, |
f035d41b XL |
2702 | ) |
2703 | }) | |
2704 | { | |
2705 | SymbolName::Link(overridden_link_name, overridden_link_name_span) | |
2706 | } else { | |
2707 | SymbolName::Normal(fi.ident.name) | |
2708 | } | |
2709 | } | |
2710 | ||
2711 | /// Checks whether two types are structurally the same enough that the declarations shouldn't | |
2712 | /// clash. We need this so we don't emit a lint when two modules both declare an extern struct, | |
2713 | /// with the same members (as the declarations shouldn't clash). | |
3dfed10e XL |
2714 | fn structurally_same_type<'tcx>( |
2715 | cx: &LateContext<'tcx>, | |
2716 | a: Ty<'tcx>, | |
2717 | b: Ty<'tcx>, | |
2718 | ckind: CItemKind, | |
2719 | ) -> bool { | |
2720 | fn structurally_same_type_impl<'tcx>( | |
2721 | seen_types: &mut FxHashSet<(Ty<'tcx>, Ty<'tcx>)>, | |
2722 | cx: &LateContext<'tcx>, | |
2723 | a: Ty<'tcx>, | |
2724 | b: Ty<'tcx>, | |
2725 | ckind: CItemKind, | |
2726 | ) -> bool { | |
2727 | debug!("structurally_same_type_impl(cx, a = {:?}, b = {:?})", a, b); | |
1b1a35ee XL |
2728 | let tcx = cx.tcx; |
2729 | ||
2730 | // Given a transparent newtype, reach through and grab the inner | |
2731 | // type unless the newtype makes the type non-null. | |
2732 | let non_transparent_ty = |ty: Ty<'tcx>| -> Ty<'tcx> { | |
2733 | let mut ty = ty; | |
2734 | loop { | |
2735 | if let ty::Adt(def, substs) = *ty.kind() { | |
04454e1e | 2736 | let is_transparent = def.repr().transparent(); |
5e7ed085 | 2737 | let is_non_null = crate::types::nonnull_optimization_guaranteed(tcx, def); |
1b1a35ee XL |
2738 | debug!( |
2739 | "non_transparent_ty({:?}) -- type is transparent? {}, type is non-null? {}", | |
2740 | ty, is_transparent, is_non_null | |
2741 | ); | |
2742 | if is_transparent && !is_non_null { | |
5e7ed085 FG |
2743 | debug_assert!(def.variants().len() == 1); |
2744 | let v = &def.variant(VariantIdx::new(0)); | |
1b1a35ee XL |
2745 | ty = transparent_newtype_field(tcx, v) |
2746 | .expect( | |
2747 | "single-variant transparent structure with zero-sized field", | |
2748 | ) | |
2749 | .ty(tcx, substs); | |
2750 | continue; | |
2751 | } | |
2752 | } | |
2753 | debug!("non_transparent_ty -> {:?}", ty); | |
2754 | return ty; | |
2755 | } | |
2756 | }; | |
2757 | ||
2758 | let a = non_transparent_ty(a); | |
2759 | let b = non_transparent_ty(b); | |
2760 | ||
3dfed10e XL |
2761 | if !seen_types.insert((a, b)) { |
2762 | // We've encountered a cycle. There's no point going any further -- the types are | |
2763 | // structurally the same. | |
2764 | return true; | |
2765 | } | |
2766 | let tcx = cx.tcx; | |
5099ac24 | 2767 | if a == b { |
3dfed10e XL |
2768 | // All nominally-same types are structurally same, too. |
2769 | true | |
2770 | } else { | |
2771 | // Do a full, depth-first comparison between the two. | |
923072b8 | 2772 | use rustc_type_ir::sty::TyKind::*; |
1b1a35ee XL |
2773 | let a_kind = a.kind(); |
2774 | let b_kind = b.kind(); | |
2775 | ||
2776 | let compare_layouts = |a, b| -> Result<bool, LayoutError<'tcx>> { | |
2777 | debug!("compare_layouts({:?}, {:?})", a, b); | |
5e7ed085 FG |
2778 | let a_layout = &cx.layout_of(a)?.layout.abi(); |
2779 | let b_layout = &cx.layout_of(b)?.layout.abi(); | |
1b1a35ee XL |
2780 | debug!( |
2781 | "comparing layouts: {:?} == {:?} = {}", | |
2782 | a_layout, | |
2783 | b_layout, | |
2784 | a_layout == b_layout | |
2785 | ); | |
2786 | Ok(a_layout == b_layout) | |
3dfed10e XL |
2787 | }; |
2788 | ||
2789 | #[allow(rustc::usage_of_ty_tykind)] | |
2790 | let is_primitive_or_pointer = |kind: &ty::TyKind<'_>| { | |
2791 | kind.is_primitive() || matches!(kind, RawPtr(..) | Ref(..)) | |
2792 | }; | |
2793 | ||
2794 | ensure_sufficient_stack(|| { | |
2795 | match (a_kind, b_kind) { | |
04454e1e | 2796 | (Adt(a_def, _), Adt(b_def, _)) => { |
1b1a35ee XL |
2797 | // We can immediately rule out these types as structurally same if |
2798 | // their layouts differ. | |
2799 | match compare_layouts(a, b) { | |
2800 | Ok(false) => return false, | |
2801 | _ => (), // otherwise, continue onto the full, fields comparison | |
2802 | } | |
2803 | ||
3dfed10e | 2804 | // Grab a flattened representation of all fields. |
5e7ed085 FG |
2805 | let a_fields = a_def.variants().iter().flat_map(|v| v.fields.iter()); |
2806 | let b_fields = b_def.variants().iter().flat_map(|v| v.fields.iter()); | |
1b1a35ee XL |
2807 | |
2808 | // Perform a structural comparison for each field. | |
2809 | a_fields.eq_by( | |
3dfed10e XL |
2810 | b_fields, |
2811 | |&ty::FieldDef { did: a_did, .. }, | |
2812 | &ty::FieldDef { did: b_did, .. }| { | |
2813 | structurally_same_type_impl( | |
2814 | seen_types, | |
2815 | cx, | |
2816 | tcx.type_of(a_did), | |
2817 | tcx.type_of(b_did), | |
2818 | ckind, | |
2819 | ) | |
2820 | }, | |
2821 | ) | |
2822 | } | |
2823 | (Array(a_ty, a_const), Array(b_ty, b_const)) => { | |
2824 | // For arrays, we also check the constness of the type. | |
923072b8 | 2825 | a_const.kind() == b_const.kind() |
5099ac24 | 2826 | && structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind) |
3dfed10e XL |
2827 | } |
2828 | (Slice(a_ty), Slice(b_ty)) => { | |
5099ac24 | 2829 | structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind) |
3dfed10e XL |
2830 | } |
2831 | (RawPtr(a_tymut), RawPtr(b_tymut)) => { | |
2832 | a_tymut.mutbl == b_tymut.mutbl | |
2833 | && structurally_same_type_impl( | |
5099ac24 | 2834 | seen_types, cx, a_tymut.ty, b_tymut.ty, ckind, |
3dfed10e XL |
2835 | ) |
2836 | } | |
2837 | (Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => { | |
2838 | // For structural sameness, we don't need the region to be same. | |
2839 | a_mut == b_mut | |
5099ac24 | 2840 | && structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind) |
3dfed10e XL |
2841 | } |
2842 | (FnDef(..), FnDef(..)) => { | |
2843 | let a_poly_sig = a.fn_sig(tcx); | |
2844 | let b_poly_sig = b.fn_sig(tcx); | |
2845 | ||
064997fb FG |
2846 | // We don't compare regions, but leaving bound regions around ICEs, so |
2847 | // we erase them. | |
2848 | let a_sig = tcx.erase_late_bound_regions(a_poly_sig); | |
2849 | let b_sig = tcx.erase_late_bound_regions(b_poly_sig); | |
3dfed10e XL |
2850 | |
2851 | (a_sig.abi, a_sig.unsafety, a_sig.c_variadic) | |
2852 | == (b_sig.abi, b_sig.unsafety, b_sig.c_variadic) | |
2853 | && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| { | |
5099ac24 | 2854 | structurally_same_type_impl(seen_types, cx, *a, *b, ckind) |
3dfed10e XL |
2855 | }) |
2856 | && structurally_same_type_impl( | |
2857 | seen_types, | |
2858 | cx, | |
2859 | a_sig.output(), | |
2860 | b_sig.output(), | |
2861 | ckind, | |
2862 | ) | |
2863 | } | |
2864 | (Tuple(a_substs), Tuple(b_substs)) => { | |
5e7ed085 | 2865 | a_substs.iter().eq_by(b_substs.iter(), |a_ty, b_ty| { |
3dfed10e XL |
2866 | structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind) |
2867 | }) | |
2868 | } | |
2869 | // For these, it's not quite as easy to define structural-sameness quite so easily. | |
2870 | // For the purposes of this lint, take the conservative approach and mark them as | |
2871 | // not structurally same. | |
2872 | (Dynamic(..), Dynamic(..)) | |
2873 | | (Error(..), Error(..)) | |
2874 | | (Closure(..), Closure(..)) | |
2875 | | (Generator(..), Generator(..)) | |
2876 | | (GeneratorWitness(..), GeneratorWitness(..)) | |
2877 | | (Projection(..), Projection(..)) | |
2878 | | (Opaque(..), Opaque(..)) => false, | |
2879 | ||
2880 | // These definitely should have been caught above. | |
2881 | (Bool, Bool) | (Char, Char) | (Never, Never) | (Str, Str) => unreachable!(), | |
2882 | ||
2883 | // An Adt and a primitive or pointer type. This can be FFI-safe if non-null | |
2884 | // enum layout optimisation is being applied. | |
2885 | (Adt(..), other_kind) | (other_kind, Adt(..)) | |
2886 | if is_primitive_or_pointer(other_kind) => | |
2887 | { | |
2888 | let (primitive, adt) = | |
1b1a35ee | 2889 | if is_primitive_or_pointer(a.kind()) { (a, b) } else { (b, a) }; |
3dfed10e XL |
2890 | if let Some(ty) = crate::types::repr_nullable_ptr(cx, adt, ckind) { |
2891 | ty == primitive | |
2892 | } else { | |
1b1a35ee | 2893 | compare_layouts(a, b).unwrap_or(false) |
3dfed10e XL |
2894 | } |
2895 | } | |
2896 | // Otherwise, just compare the layouts. This may fail to lint for some | |
2897 | // incompatible types, but at the very least, will stop reads into | |
2898 | // uninitialised memory. | |
1b1a35ee | 2899 | _ => compare_layouts(a, b).unwrap_or(false), |
3dfed10e XL |
2900 | } |
2901 | }) | |
f035d41b XL |
2902 | } |
2903 | } | |
3dfed10e XL |
2904 | let mut seen_types = FxHashSet::default(); |
2905 | structurally_same_type_impl(&mut seen_types, cx, a, b, ckind) | |
f035d41b XL |
2906 | } |
2907 | } | |
2908 | ||
2909 | impl_lint_pass!(ClashingExternDeclarations => [CLASHING_EXTERN_DECLARATIONS]); | |
2910 | ||
2911 | impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations { | |
2912 | fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, this_fi: &hir::ForeignItem<'_>) { | |
2913 | trace!("ClashingExternDeclarations: check_foreign_item: {:?}", this_fi); | |
2914 | if let ForeignItemKind::Fn(..) = this_fi.kind { | |
29967ef6 | 2915 | let tcx = cx.tcx; |
f035d41b XL |
2916 | if let Some(existing_hid) = self.insert(tcx, this_fi) { |
2917 | let existing_decl_ty = tcx.type_of(tcx.hir().local_def_id(existing_hid)); | |
6a06907d | 2918 | let this_decl_ty = tcx.type_of(this_fi.def_id); |
f035d41b XL |
2919 | debug!( |
2920 | "ClashingExternDeclarations: Comparing existing {:?}: {:?} to this {:?}: {:?}", | |
6a06907d | 2921 | existing_hid, existing_decl_ty, this_fi.def_id, this_decl_ty |
f035d41b XL |
2922 | ); |
2923 | // Check that the declarations match. | |
3dfed10e XL |
2924 | if !Self::structurally_same_type( |
2925 | cx, | |
2926 | existing_decl_ty, | |
2927 | this_decl_ty, | |
2928 | CItemKind::Declaration, | |
2929 | ) { | |
a2a8927a | 2930 | let orig_fi = tcx.hir().expect_foreign_item(existing_hid.expect_owner()); |
f035d41b XL |
2931 | let orig = Self::name_of_extern_decl(tcx, orig_fi); |
2932 | ||
2933 | // We want to ensure that we use spans for both decls that include where the | |
2934 | // name was defined, whether that was from the link_name attribute or not. | |
2935 | let get_relevant_span = | |
2936 | |fi: &hir::ForeignItem<'_>| match Self::name_of_extern_decl(tcx, fi) { | |
2937 | SymbolName::Normal(_) => fi.span, | |
2938 | SymbolName::Link(_, annot_span) => fi.span.to(annot_span), | |
2939 | }; | |
2940 | // Finally, emit the diagnostic. | |
2941 | tcx.struct_span_lint_hir( | |
2942 | CLASHING_EXTERN_DECLARATIONS, | |
6a06907d | 2943 | this_fi.hir_id(), |
f035d41b XL |
2944 | get_relevant_span(this_fi), |
2945 | |lint| { | |
2946 | let mut expected_str = DiagnosticStyledString::new(); | |
2947 | expected_str.push(existing_decl_ty.fn_sig(tcx).to_string(), false); | |
2948 | let mut found_str = DiagnosticStyledString::new(); | |
2949 | found_str.push(this_decl_ty.fn_sig(tcx).to_string(), true); | |
2950 | ||
064997fb FG |
2951 | lint.build(if orig.get_name() == this_fi.ident.name { |
2952 | fluent::lint::builtin_clashing_extern_same_name | |
2953 | } else { | |
2954 | fluent::lint::builtin_clashing_extern_diff_name | |
2955 | }) | |
2956 | .set_arg("this_fi", this_fi.ident.name) | |
2957 | .set_arg("orig", orig.get_name()) | |
f035d41b XL |
2958 | .span_label( |
2959 | get_relevant_span(orig_fi), | |
064997fb | 2960 | fluent::lint::previous_decl_label, |
f035d41b | 2961 | ) |
064997fb FG |
2962 | .span_label(get_relevant_span(this_fi), fluent::lint::mismatch_label) |
2963 | // FIXME(davidtwco): translatable expected/found | |
f035d41b | 2964 | .note_expected_found(&"", expected_str, &"", found_str) |
5e7ed085 | 2965 | .emit(); |
f035d41b XL |
2966 | }, |
2967 | ); | |
2968 | } | |
2969 | } | |
2970 | } | |
2971 | } | |
2972 | } | |
cdc7bbd5 XL |
2973 | |
2974 | declare_lint! { | |
2975 | /// The `deref_nullptr` lint detects when an null pointer is dereferenced, | |
2976 | /// which causes [undefined behavior]. | |
2977 | /// | |
2978 | /// ### Example | |
2979 | /// | |
2980 | /// ```rust,no_run | |
2981 | /// # #![allow(unused)] | |
2982 | /// use std::ptr; | |
2983 | /// unsafe { | |
2984 | /// let x = &*ptr::null::<i32>(); | |
2985 | /// let x = ptr::addr_of!(*ptr::null::<i32>()); | |
2986 | /// let x = *(0 as *const i32); | |
2987 | /// } | |
2988 | /// ``` | |
2989 | /// | |
2990 | /// {{produces}} | |
2991 | /// | |
2992 | /// ### Explanation | |
2993 | /// | |
2994 | /// Dereferencing a null pointer causes [undefined behavior] even as a place expression, | |
2995 | /// like `&*(0 as *const i32)` or `addr_of!(*(0 as *const i32))`. | |
2996 | /// | |
2997 | /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html | |
2998 | pub DEREF_NULLPTR, | |
2999 | Warn, | |
3000 | "detects when an null pointer is dereferenced" | |
3001 | } | |
3002 | ||
3003 | declare_lint_pass!(DerefNullPtr => [DEREF_NULLPTR]); | |
3004 | ||
3005 | impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { | |
3006 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) { | |
3007 | /// test if expression is a null ptr | |
3008 | fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { | |
3009 | match &expr.kind { | |
3010 | rustc_hir::ExprKind::Cast(ref expr, ref ty) => { | |
3011 | if let rustc_hir::TyKind::Ptr(_) = ty.kind { | |
3012 | return is_zero(expr) || is_null_ptr(cx, expr); | |
3013 | } | |
3014 | } | |
3015 | // check for call to `core::ptr::null` or `core::ptr::null_mut` | |
3016 | rustc_hir::ExprKind::Call(ref path, _) => { | |
3017 | if let rustc_hir::ExprKind::Path(ref qpath) = path.kind { | |
3018 | if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() { | |
c295e0f8 XL |
3019 | return matches!( |
3020 | cx.tcx.get_diagnostic_name(def_id), | |
3021 | Some(sym::ptr_null | sym::ptr_null_mut) | |
3022 | ); | |
cdc7bbd5 XL |
3023 | } |
3024 | } | |
3025 | } | |
3026 | _ => {} | |
3027 | } | |
3028 | false | |
3029 | } | |
3030 | ||
3031 | /// test if expression is the literal `0` | |
3032 | fn is_zero(expr: &hir::Expr<'_>) -> bool { | |
3033 | match &expr.kind { | |
3034 | rustc_hir::ExprKind::Lit(ref lit) => { | |
3035 | if let LitKind::Int(a, _) = lit.node { | |
3036 | return a == 0; | |
3037 | } | |
3038 | } | |
3039 | _ => {} | |
3040 | } | |
3041 | false | |
3042 | } | |
3043 | ||
3c0e092e XL |
3044 | if let rustc_hir::ExprKind::Unary(rustc_hir::UnOp::Deref, expr_deref) = expr.kind { |
3045 | if is_null_ptr(cx, expr_deref) { | |
3046 | cx.struct_span_lint(DEREF_NULLPTR, expr.span, |lint| { | |
064997fb FG |
3047 | let mut err = lint.build(fluent::lint::builtin_deref_nullptr); |
3048 | err.span_label(expr.span, fluent::lint::label); | |
3c0e092e XL |
3049 | err.emit(); |
3050 | }); | |
cdc7bbd5 XL |
3051 | } |
3052 | } | |
3053 | } | |
3054 | } | |
94222f64 XL |
3055 | |
3056 | declare_lint! { | |
3057 | /// The `named_asm_labels` lint detects the use of named labels in the | |
3058 | /// inline `asm!` macro. | |
3059 | /// | |
3060 | /// ### Example | |
3061 | /// | |
3062 | /// ```rust,compile_fail | |
a2a8927a XL |
3063 | /// use std::arch::asm; |
3064 | /// | |
94222f64 XL |
3065 | /// fn main() { |
3066 | /// unsafe { | |
3067 | /// asm!("foo: bar"); | |
3068 | /// } | |
3069 | /// } | |
3070 | /// ``` | |
3071 | /// | |
3072 | /// {{produces}} | |
3073 | /// | |
3074 | /// ### Explanation | |
3075 | /// | |
3076 | /// LLVM is allowed to duplicate inline assembly blocks for any | |
3077 | /// reason, for example when it is in a function that gets inlined. Because | |
3078 | /// of this, GNU assembler [local labels] *must* be used instead of labels | |
3079 | /// with a name. Using named labels might cause assembler or linker errors. | |
3080 | /// | |
a2a8927a | 3081 | /// See the explanation in [Rust By Example] for more details. |
94222f64 XL |
3082 | /// |
3083 | /// [local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels | |
a2a8927a | 3084 | /// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels |
94222f64 XL |
3085 | pub NAMED_ASM_LABELS, |
3086 | Deny, | |
3087 | "named labels in inline assembly", | |
3088 | } | |
3089 | ||
3090 | declare_lint_pass!(NamedAsmLabels => [NAMED_ASM_LABELS]); | |
3091 | ||
3092 | impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { | |
3093 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { | |
3094 | if let hir::Expr { | |
3095 | kind: hir::ExprKind::InlineAsm(hir::InlineAsm { template_strs, .. }), | |
3096 | .. | |
3097 | } = expr | |
3098 | { | |
3099 | for (template_sym, template_snippet, template_span) in template_strs.iter() { | |
a2a8927a | 3100 | let template_str = template_sym.as_str(); |
94222f64 XL |
3101 | let find_label_span = |needle: &str| -> Option<Span> { |
3102 | if let Some(template_snippet) = template_snippet { | |
3103 | let snippet = template_snippet.as_str(); | |
3104 | if let Some(pos) = snippet.find(needle) { | |
3105 | let end = pos | |
3c0e092e | 3106 | + snippet[pos..] |
94222f64 XL |
3107 | .find(|c| c == ':') |
3108 | .unwrap_or(snippet[pos..].len() - 1); | |
3109 | let inner = InnerSpan::new(pos, end); | |
3110 | return Some(template_span.from_inner(inner)); | |
3111 | } | |
3112 | } | |
3113 | ||
3114 | None | |
3115 | }; | |
3116 | ||
3117 | let mut found_labels = Vec::new(); | |
3118 | ||
3119 | // A semicolon might not actually be specified as a separator for all targets, but it seems like LLVM accepts it always | |
3120 | let statements = template_str.split(|c| matches!(c, '\n' | ';')); | |
3121 | for statement in statements { | |
3122 | // If there's a comment, trim it from the statement | |
3123 | let statement = statement.find("//").map_or(statement, |idx| &statement[..idx]); | |
3124 | let mut start_idx = 0; | |
3125 | for (idx, _) in statement.match_indices(':') { | |
3126 | let possible_label = statement[start_idx..idx].trim(); | |
3127 | let mut chars = possible_label.chars(); | |
a2a8927a | 3128 | let Some(c) = chars.next() else { |
94222f64 | 3129 | // Empty string means a leading ':' in this section, which is not a label |
a2a8927a XL |
3130 | break |
3131 | }; | |
3132 | // A label starts with an alphabetic character or . or _ and continues with alphanumeric characters, _, or $ | |
3133 | if (c.is_alphabetic() || matches!(c, '.' | '_')) | |
3134 | && chars.all(|c| c.is_alphanumeric() || matches!(c, '_' | '$')) | |
3135 | { | |
3136 | found_labels.push(possible_label); | |
3137 | } else { | |
3138 | // If we encounter a non-label, there cannot be any further labels, so stop checking | |
94222f64 XL |
3139 | break; |
3140 | } | |
3141 | ||
3142 | start_idx = idx + 1; | |
3143 | } | |
3144 | } | |
3145 | ||
3146 | debug!("NamedAsmLabels::check_expr(): found_labels: {:#?}", &found_labels); | |
3147 | ||
3148 | if found_labels.len() > 0 { | |
3149 | let spans = found_labels | |
3150 | .into_iter() | |
3151 | .filter_map(|label| find_label_span(label)) | |
3152 | .collect::<Vec<Span>>(); | |
3153 | // If there were labels but we couldn't find a span, combine the warnings and use the template span | |
3154 | let target_spans: MultiSpan = | |
3155 | if spans.len() > 0 { spans.into() } else { (*template_span).into() }; | |
3156 | ||
3157 | cx.lookup_with_diagnostics( | |
3158 | NAMED_ASM_LABELS, | |
3159 | Some(target_spans), | |
3160 | |diag| { | |
064997fb | 3161 | diag.build(fluent::lint::builtin_asm_labels).emit(); |
94222f64 XL |
3162 | }, |
3163 | BuiltinLintDiagnostics::NamedAsmLabel( | |
3164 | "only local labels of the form `<number>:` should be used in inline asm" | |
3165 | .to_string(), | |
3166 | ), | |
3167 | ); | |
3168 | } | |
3169 | } | |
3170 | } | |
3171 | } | |
3172 | } |