1//! Lints in the Rust compiler.2//!3//! This contains lints which can feasibly be implemented as their own4//! AST visitor. Also see `rustc_session::lint::builtin`, which contains the5//! definitions of lints that are emitted directly inside the main compiler.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//!10//! If you define a new [`EarlyLintPass`], you will also need to add it to the11//! [`crate::early_lint_methods!`] invocation in `lib.rs`.12//!13//! If you define a new [`LateLintPass`], you will also need to add it to the14//! [`crate::late_lint_methods!`] invocation in `lib.rs`.1516use std::fmt::Write;1718use ast::token::TokenKind;19use rustc_abi::BackendRepr;20use rustc_ast::tokenstream::{TokenStream, TokenTree};21use rustc_ast::visit::{FnCtxt, FnKind};22use rustc_ast::{self as ast, *};23use rustc_ast_pretty::pprust::expr_to_string;24use rustc_attr_parsing::AttributeParser;25use rustc_errors::{Applicability, Diagnostic, msg};26use rustc_feature::GateIssue;27use rustc_hir::attrs::{AttributeKind, DocAttribute};28use rustc_hir::def::{DefKind, Res};29use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};30use rustc_hir::intravisit::FnKind as HirFnKind;31use rustc_hir::{self as hir, Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr};32use rustc_middle::bug;33use rustc_middle::ty::layout::LayoutOf;34use rustc_middle::ty::print::with_no_trimmed_paths;35use rustc_middle::ty::{36 self, AssocContainer, Ty, TyCtxt, TypeVisitableExt, Unnormalized, Upcast, VariantDef,37};38// hardwired lints from rustc_lint_defs39pub use rustc_session::lint::builtin::*;40use rustc_session::lint::fcw;41use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};42use rustc_span::edition::Edition;43use rustc_span::{DUMMY_SP, Ident, InnerSpan, Span, Spanned, Symbol, kw, sym};44use rustc_target::asm::InlineAsmArch;45use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};46use rustc_trait_selection::traits;47use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy;48use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;4950use crate::diagnostics::BuiltinEllipsisInclusiveRangePatterns;51use crate::lints::{52 BuiltinAnonymousParams, BuiltinConstNoMangle, BuiltinDerefNullptr, BuiltinDoubleNegations,53 BuiltinDoubleNegationsAddParens, BuiltinEllipsisInclusiveRangePatternsLint,54 BuiltinExplicitOutlives, BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote,55 BuiltinIncompleteFeatures, BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures,56 BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,57 BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,58 BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds,59 BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub,60 BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment,61 BuiltinUnusedDocCommentSub, BuiltinWhileTrue, EqInternalMethodImplemented, InvalidAsmLabel,62};63use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};6465declare_lint! {66 /// The `while_true` lint detects `while true { }`.67 ///68 /// ### Example69 ///70 /// ```rust,no_run71 /// while true {72 ///73 /// }74 /// ```75 ///76 /// {{produces}}77 ///78 /// ### Explanation79 ///80 /// `while true` should be replaced with `loop`. A `loop` expression is81 /// the preferred way to write an infinite loop because it more directly82 /// expresses the intent of the loop.83 WHILE_TRUE,84 Warn,85 "suggest using `loop { }` instead of `while true { }`"86}8788declare_lint_pass!(WhileTrue => [WHILE_TRUE]);8990impl EarlyLintPass for WhileTrue {91 #[inline]92 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {93 if let ast::ExprKind::While(cond, _, label) = &e.kind94 && let ast::ExprKind::Lit(token_lit) = cond.peel_parens().kind95 && let token::Lit { kind: token::Bool, symbol: kw::True, .. } = token_lit96 && !cond.span.from_expansion()97 {98 let condition_span = e.span.with_hi(cond.span.hi());99 let replace = format!(100 "{}loop",101 label.map_or_else(String::new, |label| format!("{}: ", label.ident,))102 );103 cx.emit_span_lint(104 WHILE_TRUE,105 condition_span,106 BuiltinWhileTrue { suggestion: condition_span, replace },107 );108 }109 }110}111112declare_lint! {113 /// The `non_shorthand_field_patterns` lint detects using `Struct { x: x }`114 /// instead of `Struct { x }` in a pattern.115 ///116 /// ### Example117 ///118 /// ```rust119 /// struct Point {120 /// x: i32,121 /// y: i32,122 /// }123 ///124 ///125 /// fn main() {126 /// let p = Point {127 /// x: 5,128 /// y: 5,129 /// };130 ///131 /// match p {132 /// Point { x: x, y: y } => (),133 /// }134 /// }135 /// ```136 ///137 /// {{produces}}138 ///139 /// ### Explanation140 ///141 /// The preferred style is to avoid the repetition of specifying both the142 /// field name and the binding name if both identifiers are the same.143 NON_SHORTHAND_FIELD_PATTERNS,144 Warn,145 "using `Struct { x: x }` instead of `Struct { x }` in a pattern"146}147148declare_lint_pass!(NonShorthandFieldPatterns => [NON_SHORTHAND_FIELD_PATTERNS]);149150impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns {151 fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) {152 // The result shouldn't be tainted, otherwise it will cause ICE.153 if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind154 && cx.typeck_results().tainted_by_errors.is_none()155 {156 let variant = cx157 .typeck_results()158 .pat_ty(pat)159 .ty_adt_def()160 .expect("struct pattern type is not an ADT")161 .variant_of_res(cx.qpath_res(qpath, pat.hir_id));162 for fieldpat in field_pats {163 if fieldpat.is_shorthand {164 continue;165 }166 if fieldpat.span.from_expansion() {167 // Don't lint if this is a macro expansion: macro authors168 // shouldn't have to worry about this kind of style issue169 // (Issue #49588)170 continue;171 }172 if let PatKind::Binding(binding_annot, _, ident, None) = fieldpat.pat.kind {173 if cx.tcx.find_field_index(ident, variant)174 == Some(cx.typeck_results().field_index(fieldpat.hir_id))175 {176 cx.emit_span_lint(177 NON_SHORTHAND_FIELD_PATTERNS,178 fieldpat.span,179 BuiltinNonShorthandFieldPatterns {180 ident,181 suggestion: fieldpat.span,182 prefix: binding_annot.prefix_str(),183 },184 );185 }186 }187 }188 }189 }190}191192declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]);193194impl UnsafeCode {195 fn report_unsafe(196 &self,197 cx: &EarlyContext<'_>,198 span: Span,199 decorate: impl for<'a> Diagnostic<'a, ()>,200 ) {201 // This comes from a macro that has `#[allow_internal_unsafe]`.202 if span.allows_unsafe() {203 return;204 }205206 cx.emit_span_lint(UNSAFE_CODE, span, decorate);207 }208}209210impl EarlyLintPass for UnsafeCode {211 #[inline]212 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {213 if let ast::ExprKind::Block(ref blk, _) = e.kind {214 // Don't warn about generated blocks; that'll just pollute the output.215 if blk.rules == ast::BlockCheckMode::Unsafe(ast::UserProvided) {216 self.report_unsafe(cx, blk.span, BuiltinUnsafe::UnsafeBlock);217 }218 }219 }220221 fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {222 match it.kind {223 ast::ItemKind::Trait(ast::Trait { safety: ast::Safety::Unsafe(_), .. }) => {224 self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeTrait);225 }226227 ast::ItemKind::Impl(ast::Impl {228 of_trait: Some(ast::TraitImplHeader { safety: ast::Safety::Unsafe(_), .. }),229 ..230 }) => {231 self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeImpl);232 }233234 ast::ItemKind::GlobalAsm(..) => {235 self.report_unsafe(cx, it.span, BuiltinUnsafe::GlobalAsm);236 }237238 ast::ItemKind::ForeignMod(ForeignMod { safety, .. }) => {239 if let Safety::Unsafe(_) = safety {240 self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeExternBlock);241 }242 }243244 ast::ItemKind::MacroDef(..) => {245 if let Some(hir::Attribute::Parsed(AttributeKind::AllowInternalUnsafe(span))) =246 AttributeParser::parse_limited(247 cx.builder.sess(),248 &it.attrs,249 &[sym::allow_internal_unsafe],250 )251 {252 self.report_unsafe(cx, span, BuiltinUnsafe::AllowInternalUnsafe);253 }254 }255256 _ => {}257 }258 }259260 fn check_fn(&mut self, cx: &EarlyContext<'_>, fk: FnKind<'_>, span: Span, _: ast::NodeId) {261 if let FnKind::Fn(262 ctxt,263 _,264 ast::Fn {265 sig: ast::FnSig { header: ast::FnHeader { safety: ast::Safety::Unsafe(_), .. }, .. },266 body,267 ..268 },269 ) = fk270 {271 let decorator = match ctxt {272 FnCtxt::Foreign => return,273 FnCtxt::Free => BuiltinUnsafe::DeclUnsafeFn,274 FnCtxt::Assoc(_) if body.is_none() => BuiltinUnsafe::DeclUnsafeMethod,275 FnCtxt::Assoc(_) => BuiltinUnsafe::ImplUnsafeMethod,276 };277 self.report_unsafe(cx, span, decorator);278 }279 }280}281282declare_lint! {283 /// The `missing_docs` lint detects missing documentation for public items.284 ///285 /// ### Example286 ///287 /// ```rust,compile_fail288 /// #![deny(missing_docs)]289 /// pub fn foo() {}290 /// ```291 ///292 /// {{produces}}293 ///294 /// ### Explanation295 ///296 /// This lint is intended to ensure that a library is well-documented.297 /// Items without documentation can be difficult for users to understand298 /// how to use properly.299 ///300 /// This lint is "allow" by default because it can be noisy, and not all301 /// projects may want to enforce everything to be documented.302 pub MISSING_DOCS,303 Allow,304 "detects missing documentation for public members",305 report_in_external_macro306}307308#[derive(Default)]309pub struct MissingDoc;310311impl_lint_pass!(MissingDoc => [MISSING_DOCS]);312313fn has_doc(attr: &hir::Attribute) -> bool {314 if matches!(attr, hir::Attribute::Parsed(AttributeKind::DocComment { .. })) {315 return true;316 }317318 if let hir::Attribute::Parsed(AttributeKind::Doc(d)) = attr319 && matches!(d.as_ref(), DocAttribute { hidden: Some(..), .. })320 {321 return true;322 }323324 false325}326327impl MissingDoc {328 fn check_missing_docs_attrs(329 &self,330 cx: &LateContext<'_>,331 def_id: LocalDefId,332 article: &'static str,333 desc: &'static str,334 ) {335 // Only check publicly-visible items, using the result from the privacy pass.336 // It's an option so the crate root can also use this function (it doesn't337 // have a `NodeId`).338 if def_id != CRATE_DEF_ID && !cx.effective_visibilities.is_exported(def_id) {339 return;340 }341342 let attrs = cx.tcx.hir_attrs(cx.tcx.local_def_id_to_hir_id(def_id));343 let has_doc = attrs.iter().any(has_doc);344 if !has_doc {345 cx.emit_span_lint(346 MISSING_DOCS,347 cx.tcx.def_span(def_id),348 BuiltinMissingDoc { article, desc },349 );350 }351 }352}353354impl<'tcx> LateLintPass<'tcx> for MissingDoc {355 fn check_crate(&mut self, cx: &LateContext<'_>) {356 self.check_missing_docs_attrs(cx, CRATE_DEF_ID, "the", "crate");357 }358359 fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {360 // Previously the Impl and Use types have been excluded from missing docs,361 // so we will continue to exclude them for compatibility.362 //363 // The documentation on `ExternCrate` is not used at the moment so no need to warn for it.364 if let hir::ItemKind::Impl(..) | hir::ItemKind::Use(..) | hir::ItemKind::ExternCrate(..) =365 it.kind366 {367 return;368 }369370 let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id());371 self.check_missing_docs_attrs(cx, it.owner_id.def_id, article, desc);372 }373374 fn check_trait_item(&mut self, cx: &LateContext<'_>, trait_item: &hir::TraitItem<'_>) {375 let (article, desc) = cx.tcx.article_and_description(trait_item.owner_id.to_def_id());376377 self.check_missing_docs_attrs(cx, trait_item.owner_id.def_id, article, desc);378 }379380 fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {381 let container = cx.tcx.associated_item(impl_item.owner_id.def_id).container;382383 match container {384 // If the method is an impl for a trait, don't doc.385 AssocContainer::TraitImpl(_) => return,386 AssocContainer::Trait => {}387 // If the method is an impl for an item with docs_hidden, don't doc.388 AssocContainer::InherentImpl => {389 let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id());390 let impl_ty = cx.tcx.type_of(parent).instantiate_identity().skip_norm_wip();391 let outerdef = match impl_ty.kind() {392 ty::Adt(def, _) => Some(def.did()),393 ty::Foreign(def_id) => Some(*def_id),394 _ => None,395 };396 let is_hidden = match outerdef {397 Some(id) => cx.tcx.is_doc_hidden(id),398 None => false,399 };400 if is_hidden {401 return;402 }403 }404 }405406 let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id());407 self.check_missing_docs_attrs(cx, impl_item.owner_id.def_id, article, desc);408 }409410 fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'_>) {411 let (article, desc) = cx.tcx.article_and_description(foreign_item.owner_id.to_def_id());412 self.check_missing_docs_attrs(cx, foreign_item.owner_id.def_id, article, desc);413 }414415 fn check_field_def(&mut self, cx: &LateContext<'_>, sf: &hir::FieldDef<'_>) {416 if !sf.is_positional() {417 self.check_missing_docs_attrs(cx, sf.def_id, "a", "struct field")418 }419 }420421 fn check_variant(&mut self, cx: &LateContext<'_>, v: &hir::Variant<'_>) {422 self.check_missing_docs_attrs(cx, v.def_id, "a", "variant");423 }424}425426declare_lint! {427 /// The `missing_copy_implementations` lint detects potentially-forgotten428 /// implementations of [`Copy`] for public types.429 ///430 /// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html431 ///432 /// ### Example433 ///434 /// ```rust,compile_fail435 /// #![deny(missing_copy_implementations)]436 /// pub struct Foo {437 /// pub field: i32438 /// }439 /// # fn main() {}440 /// ```441 ///442 /// {{produces}}443 ///444 /// ### Explanation445 ///446 /// Historically (before 1.0), types were automatically marked as `Copy`447 /// if possible. This was changed so that it required an explicit opt-in448 /// by implementing the `Copy` trait. As part of this change, a lint was449 /// added to alert if a copyable type was not marked `Copy`.450 ///451 /// This lint is "allow" by default because this code isn't bad; it is452 /// common to write newtypes like this specifically so that a `Copy` type453 /// is no longer `Copy`. `Copy` types can result in unintended copies of454 /// large data which can impact performance.455 pub MISSING_COPY_IMPLEMENTATIONS,456 Allow,457 "detects potentially-forgotten implementations of `Copy`"458}459460declare_lint_pass!(MissingCopyImplementations => [MISSING_COPY_IMPLEMENTATIONS]);461462impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {463 fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {464 if !cx.effective_visibilities.is_reachable(item.owner_id.def_id) {465 return;466 }467 let (def, ty) = match item.kind {468 hir::ItemKind::Struct(_, generics, _) => {469 if !generics.params.is_empty() {470 return;471 }472 let def = cx.tcx.adt_def(item.owner_id);473 (def, Ty::new_adt(cx.tcx, def, ty::List::empty()))474 }475 hir::ItemKind::Union(_, generics, _) => {476 if !generics.params.is_empty() {477 return;478 }479 let def = cx.tcx.adt_def(item.owner_id);480 (def, Ty::new_adt(cx.tcx, def, ty::List::empty()))481 }482 hir::ItemKind::Enum(_, generics, _) => {483 if !generics.params.is_empty() {484 return;485 }486 let def = cx.tcx.adt_def(item.owner_id);487 (def, Ty::new_adt(cx.tcx, def, ty::List::empty()))488 }489 _ => return,490 };491 if def.has_dtor(cx.tcx) {492 return;493 }494495 // If the type contains a raw pointer, it may represent something like a handle,496 // and recommending Copy might be a bad idea.497 for field in def.all_fields() {498 let did = field.did;499 if cx.tcx.type_of(did).instantiate_identity().skip_norm_wip().is_raw_ptr() {500 return;501 }502 }503 if cx.type_is_copy_modulo_regions(ty) {504 return;505 }506 if type_implements_negative_copy_modulo_regions(cx.tcx, ty, cx.typing_env()) {507 return;508 }509 if def.is_variant_list_non_exhaustive()510 || def.variants().iter().any(|variant| variant.is_field_list_non_exhaustive())511 {512 return;513 }514515 // We shouldn't recommend implementing `Copy` on stateful things,516 // such as iterators.517 if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)518 && cx519 .tcx520 .infer_ctxt()521 .build(cx.typing_mode())522 .type_implements_trait(iter_trait, [ty], cx.param_env)523 .must_apply_modulo_regions()524 {525 return;526 }527528 // Default value of clippy::trivially_copy_pass_by_ref529 const MAX_SIZE: u64 = 256;530531 if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()) {532 if size > MAX_SIZE {533 return;534 }535 }536537 if type_allowed_to_implement_copy(538 cx.tcx,539 cx.param_env,540 ty,541 traits::ObligationCause::misc(item.span, item.owner_id.def_id),542 hir::Safety::Safe,543 )544 .is_ok()545 {546 cx.emit_span_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, BuiltinMissingCopyImpl);547 }548 }549}550551/// Check whether a `ty` has a negative `Copy` implementation, ignoring outlives constraints.552fn type_implements_negative_copy_modulo_regions<'tcx>(553 tcx: TyCtxt<'tcx>,554 ty: Ty<'tcx>,555 typing_env: ty::TypingEnv<'tcx>,556) -> bool {557 let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);558 let trait_ref =559 ty::TraitRef::new(tcx, tcx.require_lang_item(hir::LangItem::Copy, DUMMY_SP), [ty]);560 let pred = ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Negative };561 let obligation = traits::Obligation {562 cause: traits::ObligationCause::dummy(),563 param_env,564 recursion_depth: 0,565 predicate: pred.upcast(tcx),566 };567 infcx.predicate_must_hold_modulo_regions(&obligation)568}569570declare_lint! {571 /// The `missing_debug_implementations` lint detects missing572 /// implementations of [`fmt::Debug`] for public types.573 ///574 /// [`fmt::Debug`]: https://doc.rust-lang.org/std/fmt/trait.Debug.html575 ///576 /// ### Example577 ///578 /// ```rust,compile_fail579 /// #![deny(missing_debug_implementations)]580 /// pub struct Foo;581 /// # fn main() {}582 /// ```583 ///584 /// {{produces}}585 ///586 /// ### Explanation587 ///588 /// Having a `Debug` implementation on all types can assist with589 /// debugging, as it provides a convenient way to format and display a590 /// value. Using the `#[derive(Debug)]` attribute will automatically591 /// generate a typical implementation, or a custom implementation can be592 /// added by manually implementing the `Debug` trait.593 ///594 /// This lint is "allow" by default because adding `Debug` to all types can595 /// have a negative impact on compile time and code size. It also requires596 /// boilerplate to be added to every type, which can be an impediment.597 MISSING_DEBUG_IMPLEMENTATIONS,598 Allow,599 "detects missing implementations of Debug"600}601602#[derive(Default)]603pub(crate) struct MissingDebugImplementations;604605impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]);606607impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {608 fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {609 let def_id = item.owner_id.def_id;610 if !cx.effective_visibilities.is_reachable(def_id) {611 return;612 }613614 let is_generic = match item.kind {615 hir::ItemKind::Struct(_, generics, _)616 | hir::ItemKind::Union(_, generics, _)617 | hir::ItemKind::Enum(_, generics, _) => !generics.params.is_empty(),618 _ => return,619 };620621 let tcx = cx.tcx;622623 // Avoid listing trait impls if the trait is allowed.624 if tcx.lint_level_spec_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id()).is_allow() {625 return;626 }627628 let Some(debug) = tcx.get_diagnostic_item(sym::Debug) else { return };629630 let ty = tcx.type_of(item.owner_id);631 if tcx632 .non_blanket_impls_for_ty(debug, ty.instantiate_identity().skip_norm_wip())633 .next()634 .is_some()635 {636 return;637 }638639 let infcx = tcx.infer_ctxt().build(cx.typing_mode());640 if is_generic {641 let args = infcx.fresh_args_for_item(item.span, def_id.to_def_id());642 if infcx643 .type_implements_trait_shallow(644 debug,645 ty.instantiate(tcx, args).skip_norm_wip(),646 cx.param_env,647 )648 .is_some()649 {650 return;651 }652 } else if infcx653 .type_implements_trait(debug, [ty.instantiate_identity().skip_norm_wip()], cx.param_env)654 .must_apply_modulo_regions()655 {656 return;657 }658659 cx.emit_span_lint(660 MISSING_DEBUG_IMPLEMENTATIONS,661 item.span,662 BuiltinMissingDebugImpl { tcx: cx.tcx, def_id: debug },663 );664 }665}666667declare_lint! {668 /// The `anonymous_parameters` lint detects anonymous parameters in trait669 /// definitions.670 ///671 /// ### Example672 ///673 /// ```rust,edition2015,compile_fail674 /// #![deny(anonymous_parameters)]675 /// // edition 2015676 /// pub trait Foo {677 /// fn foo(usize);678 /// }679 /// fn main() {}680 /// ```681 ///682 /// {{produces}}683 ///684 /// ### Explanation685 ///686 /// This syntax is mostly a historical accident, and can be worked around687 /// quite easily by adding an `_` pattern or a descriptive identifier:688 ///689 /// ```rust690 /// trait Foo {691 /// fn foo(_: usize);692 /// }693 /// ```694 ///695 /// This syntax is now a hard error in the 2018 edition. In the 2015696 /// edition, this lint is "warn" by default. This lint697 /// enables the [`cargo fix`] tool with the `--edition` flag to698 /// automatically transition old code from the 2015 edition to 2018. The699 /// tool will run this lint and automatically apply the700 /// suggested fix from the compiler (which is to add `_` to each701 /// parameter). This provides a completely automated way to update old702 /// code for a new edition. See [issue #41686] for more details.703 ///704 /// [issue #41686]: https://github.com/rust-lang/rust/issues/41686705 /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html706 pub ANONYMOUS_PARAMETERS,707 Warn,708 "detects anonymous parameters",709 @future_incompatible = FutureIncompatibleInfo {710 reason: fcw!(EditionError 2018 "trait-fn-parameters"),711 };712}713714declare_lint_pass!(715 /// Checks for use of anonymous parameters (RFC 1685).716 AnonymousParameters => [ANONYMOUS_PARAMETERS]717);718719impl EarlyLintPass for AnonymousParameters {720 fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) {721 if cx.sess().edition() != Edition::Edition2015 {722 // This is a hard error in future editions; avoid linting and erroring723 return;724 }725 if let ast::AssocItemKind::Fn(Fn { ref sig, .. }) = it.kind {726 for arg in sig.decl.inputs.iter() {727 if let ast::PatKind::Missing = arg.pat.kind {728 let ty_snip = cx.sess().source_map().span_to_snippet(arg.ty.span);729730 let (ty_snip, appl) = if let Ok(ref snip) = ty_snip {731 (snip.as_str(), Applicability::MachineApplicable)732 } else {733 ("<type>", Applicability::HasPlaceholders)734 };735 cx.emit_span_lint(736 ANONYMOUS_PARAMETERS,737 arg.pat.span,738 BuiltinAnonymousParams { suggestion: (arg.pat.span, appl), ty_snip },739 );740 }741 }742 }743 }744}745746fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &[ast::Attribute]) {747 use rustc_ast::token::CommentKind;748749 let mut attrs = attrs.iter().peekable();750751 // Accumulate a single span for sugared doc comments.752 let mut sugared_span: Option<Span> = None;753754 while let Some(attr) = attrs.next() {755 let (is_doc_comment, is_doc_attribute) = match &attr.kind {756 AttrKind::DocComment(..) => (true, false),757 AttrKind::Normal(normal) if normal.item.path == sym::doc => (true, true),758 _ => (false, false),759 };760 if is_doc_comment {761 sugared_span =762 Some(sugared_span.map_or(attr.span, |span| span.with_hi(attr.span.hi())));763 }764765 if !is_doc_attribute && attrs.peek().is_some_and(|next_attr| next_attr.is_doc_comment()) {766 continue;767 }768769 let span = sugared_span.take().unwrap_or(attr.span);770771 if is_doc_comment || is_doc_attribute {772 let sub = match attr.kind {773 AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => {774 BuiltinUnusedDocCommentSub::PlainHelp775 }776 AttrKind::DocComment(CommentKind::Block, _) => {777 BuiltinUnusedDocCommentSub::BlockHelp778 }779 };780 cx.emit_span_lint(781 UNUSED_DOC_COMMENTS,782 span,783 BuiltinUnusedDocComment { kind: node_kind, label: node_span, sub },784 );785 }786 }787}788789impl EarlyLintPass for UnusedDocComment {790 fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {791 let kind = match stmt.kind {792 ast::StmtKind::Let(..) => "statements",793 // Disabled pending discussion in #78306794 ast::StmtKind::Item(..) => return,795 // expressions will be reported by `check_expr`.796 ast::StmtKind::Empty797 | ast::StmtKind::Semi(_)798 | ast::StmtKind::Expr(_)799 | ast::StmtKind::MacCall(_) => return,800 };801802 warn_if_doc(cx, stmt.span, kind, stmt.kind.attrs());803 }804805 fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {806 if let Some(body) = &arm.body {807 let arm_span = arm.pat.span.with_hi(body.span.hi());808 warn_if_doc(cx, arm_span, "match arms", &arm.attrs);809 }810 }811812 fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &ast::Pat) {813 if let ast::PatKind::Struct(_, _, fields, _) = &pat.kind {814 for field in fields {815 warn_if_doc(cx, field.span, "pattern fields", &field.attrs);816 }817 }818 }819820 fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {821 warn_if_doc(cx, expr.span, "expressions", &expr.attrs);822823 if let ExprKind::Struct(s) = &expr.kind {824 for field in &s.fields {825 warn_if_doc(cx, field.span, "expression fields", &field.attrs);826 }827 }828 }829830 fn check_generic_param(&mut self, cx: &EarlyContext<'_>, param: &ast::GenericParam) {831 warn_if_doc(cx, param.ident.span, "generic parameters", ¶m.attrs);832 }833834 fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {835 warn_if_doc(cx, block.span, "blocks", block.attrs());836 }837838 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {839 if let ast::ItemKind::ForeignMod(_) = item.kind {840 warn_if_doc(cx, item.span, "extern blocks", &item.attrs);841 }842 }843}844845declare_lint! {846 /// The `no_mangle_const_items` lint detects any `const` items with the847 /// [`no_mangle` attribute].848 ///849 /// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute850 ///851 /// ### Example852 ///853 /// ```rust,compile_fail,edition2021854 /// #[no_mangle]855 /// const FOO: i32 = 5;856 /// ```857 ///858 /// {{produces}}859 ///860 /// ### Explanation861 ///862 /// Constants do not have their symbols exported, and therefore, this863 /// probably means you meant to use a [`static`], not a [`const`].864 ///865 /// [`static`]: https://doc.rust-lang.org/reference/items/static-items.html866 /// [`const`]: https://doc.rust-lang.org/reference/items/constant-items.html867 NO_MANGLE_CONST_ITEMS,868 Deny,869 "const items will not have their symbols exported"870}871872declare_lint! {873 /// The `no_mangle_generic_items` lint detects generic items that must be874 /// mangled.875 ///876 /// ### Example877 ///878 /// ```rust879 /// #[unsafe(no_mangle)]880 /// fn foo<T>(t: T) {}881 ///882 /// #[unsafe(export_name = "bar")]883 /// fn bar<T>(t: T) {}884 /// ```885 ///886 /// {{produces}}887 ///888 /// ### Explanation889 ///890 /// A function with generics must have its symbol mangled to accommodate891 /// the generic parameter. The [`no_mangle`] and [`export_name`] attributes892 /// have no effect in this situation, and should be removed.893 ///894 /// [`no_mangle`]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute895 /// [`export_name`]: https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute896 NO_MANGLE_GENERIC_ITEMS,897 Warn,898 "generic items must be mangled"899}900901declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GENERIC_ITEMS]);902903impl InvalidNoMangleItems {904 fn check_no_mangle_on_generic_fn(905 &self,906 cx: &LateContext<'_>,907 attr_span: Span,908 def_id: LocalDefId,909 ) {910 let generics = cx.tcx.generics_of(def_id);911 if generics.requires_monomorphization(cx.tcx) {912 cx.emit_span_lint(913 NO_MANGLE_GENERIC_ITEMS,914 cx.tcx.def_span(def_id),915 BuiltinNoMangleGeneric { suggestion: attr_span },916 );917 }918 }919}920921impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {922 fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {923 let attrs = cx.tcx.hir_attrs(it.hir_id());924 match it.kind {925 hir::ItemKind::Fn { .. } => {926 if let Some(attr_span) = find_attr!(attrs, ExportName {span, ..} => *span)927 .or_else(|| find_attr!(attrs, NoMangle(span) => *span))928 {929 self.check_no_mangle_on_generic_fn(cx, attr_span, it.owner_id.def_id);930 }931 }932 hir::ItemKind::Const(ident, generics, ..) => {933 if find_attr!(attrs, NoMangle(..)) {934 let suggestion =935 if generics.params.is_empty() && generics.where_clause_span.is_empty() {936 // account for "pub const" (#45562)937 Some(it.span.until(ident.span))938 } else {939 None940 };941942 // Const items do not refer to a particular location in memory, and therefore943 // don't have anything to attach a symbol to944 cx.emit_span_lint(945 NO_MANGLE_CONST_ITEMS,946 it.span,947 BuiltinConstNoMangle { suggestion },948 );949 }950 }951 _ => {}952 }953 }954955 fn check_impl_item(&mut self, cx: &LateContext<'_>, it: &hir::ImplItem<'_>) {956 let attrs = cx.tcx.hir_attrs(it.hir_id());957 match it.kind {958 hir::ImplItemKind::Fn { .. } => {959 if let Some(attr_span) = find_attr!(attrs, ExportName {span, ..} => *span)960 .or_else(|| find_attr!(attrs, NoMangle(span) => *span))961 {962 self.check_no_mangle_on_generic_fn(cx, attr_span, it.owner_id.def_id);963 }964 }965 _ => {}966 }967 }968}969970declare_lint! {971 /// The `mutable_transmutes` lint catches transmuting from `&T` to `&mut972 /// T` because it is [undefined behavior].973 ///974 /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html975 ///976 /// ### Example977 ///978 /// ```rust,compile_fail979 /// unsafe {980 /// let y = std::mem::transmute::<&i32, &mut i32>(&5);981 /// }982 /// ```983 ///984 /// {{produces}}985 ///986 /// ### Explanation987 ///988 /// Certain assumptions are made about aliasing of data, and this transmute989 /// violates those assumptions. Consider using [`UnsafeCell`] instead.990 ///991 /// [`UnsafeCell`]: https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html992 MUTABLE_TRANSMUTES,993 Deny,994 "transmuting &T to &mut T is undefined behavior, even if the reference is unused"995}996997declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]);998999impl<'tcx> LateLintPass<'tcx> for MutableTransmutes {1000 fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {1001 if let Some((&ty::Ref(_, _, from_mutbl), &ty::Ref(_, _, to_mutbl))) =1002 get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind()))1003 {1004 if from_mutbl < to_mutbl {1005 cx.emit_span_lint(MUTABLE_TRANSMUTES, expr.span, BuiltinMutablesTransmutes);1006 }1007 }10081009 fn get_transmute_from_to<'tcx>(1010 cx: &LateContext<'tcx>,1011 expr: &hir::Expr<'_>,1012 ) -> Option<(Ty<'tcx>, Ty<'tcx>)> {1013 let hir::ExprKind::Path(ref qpath) = expr.kind else { return None };1014 let def = cx.qpath_res(qpath, expr.hir_id);1015 if let Res::Def(DefKind::Fn, did) = def {1016 if !def_id_is_transmute(cx, did) {1017 return None;1018 }1019 let sig = cx.typeck_results().node_type(expr.hir_id).fn_sig(cx.tcx);1020 let from = sig.inputs().skip_binder()[0];1021 let to = sig.output().skip_binder();1022 return Some((from, to));1023 }1024 None1025 }10261027 fn def_id_is_transmute(cx: &LateContext<'_>, def_id: DefId) -> bool {1028 cx.tcx.is_intrinsic(def_id, sym::transmute)1029 }1030 }1031}10321033declare_lint! {1034 /// The `unstable_features` lint detects uses of `#![feature]`.1035 ///1036 /// ### Example1037 ///1038 /// ```rust,compile_fail1039 /// #![deny(unstable_features)]1040 /// #![feature(test)]1041 /// ```1042 ///1043 /// {{produces}}1044 ///1045 /// ### Explanation1046 ///1047 /// In larger nightly-based projects which1048 ///1049 /// * consist of a multitude of crates where a subset of crates has to compile on1050 /// stable either unconditionally or depending on a `cfg` flag to for example1051 /// allow stable users to depend on them,1052 /// * don't use nightly for experimental features but for, e.g., unstable options only,1053 ///1054 /// this lint may come in handy to enforce policies of these kinds.1055 UNSTABLE_FEATURES,1056 Allow,1057 "enabling unstable features"1058}10591060declare_lint_pass!(1061 /// Forbids using the `#[feature(...)]` attribute1062 UnstableFeatures => [UNSTABLE_FEATURES]1063);10641065impl<'tcx> LateLintPass<'tcx> for UnstableFeatures {1066 fn check_attributes(&mut self, cx: &LateContext<'_>, attrs: &[hir::Attribute]) {1067 if let Some(features) = find_attr!(attrs, Feature(features, _) => features) {1068 for feature in features {1069 cx.emit_span_lint(UNSTABLE_FEATURES, feature.span, BuiltinUnstableFeatures);1070 }1071 }1072 }1073}10741075declare_lint! {1076 /// The `ungated_async_fn_track_caller` lint warns when the1077 /// `#[track_caller]` attribute is used on an async function1078 /// without enabling the corresponding unstable feature flag.1079 ///1080 /// ### Example1081 ///1082 /// ```rust1083 /// #[track_caller]1084 /// async fn foo() {}1085 /// ```1086 ///1087 /// {{produces}}1088 ///1089 /// ### Explanation1090 ///1091 /// The attribute must be used in conjunction with the1092 /// [`async_fn_track_caller` feature flag]. Otherwise, the `#[track_caller]`1093 /// annotation will function as a no-op.1094 ///1095 /// [`async_fn_track_caller` feature flag]: https://doc.rust-lang.org/beta/unstable-book/language-features/async-fn-track-caller.html1096 UNGATED_ASYNC_FN_TRACK_CALLER,1097 Warn,1098 "enabling track_caller on an async fn is a no-op unless the async_fn_track_caller feature is enabled"1099}11001101declare_lint_pass!(1102 /// Explains corresponding feature flag must be enabled for the `#[track_caller]` attribute to1103 /// do anything1104 UngatedAsyncFnTrackCaller => [UNGATED_ASYNC_FN_TRACK_CALLER]1105);11061107impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {1108 fn check_fn(1109 &mut self,1110 cx: &LateContext<'_>,1111 fn_kind: HirFnKind<'_>,1112 _: &'tcx FnDecl<'_>,1113 _: &'tcx Body<'_>,1114 span: Span,1115 def_id: LocalDefId,1116 ) {1117 if fn_kind.asyncness().is_async()1118 && !cx.tcx.features().async_fn_track_caller()1119 // Now, check if the function has the `#[track_caller]` attribute1120 && let Some(attr_span) = find_attr!(cx.tcx, def_id, TrackCaller(span) => *span)1121 {1122 cx.emit_span_lint(1123 UNGATED_ASYNC_FN_TRACK_CALLER,1124 attr_span,1125 BuiltinUngatedAsyncFnTrackCaller { label: span, session: &cx.tcx.sess },1126 );1127 }1128 }1129}11301131declare_lint! {1132 /// The `unreachable_pub` lint triggers for `pub` items not reachable from other crates - that1133 /// means neither directly accessible, nor reexported (with `pub use`), nor leaked through1134 /// things like return types (which the [`unnameable_types`] lint can detect if desired).1135 ///1136 /// ### Example1137 ///1138 /// ```rust,compile_fail1139 /// #![deny(unreachable_pub)]1140 /// mod foo {1141 /// pub mod bar {1142 ///1143 /// }1144 /// }1145 /// ```1146 ///1147 /// {{produces}}1148 ///1149 /// ### Explanation1150 ///1151 /// The `pub` keyword both expresses an intent for an item to be publicly available, and also1152 /// signals to the compiler to make the item publicly accessible. The intent can only be1153 /// satisfied, however, if all items which contain this item are *also* publicly accessible.1154 /// Thus, this lint serves to identify situations where the intent does not match the reality.1155 ///1156 /// If you wish the item to be accessible elsewhere within the crate, but not outside it, the1157 /// `pub(crate)` visibility is recommended to be used instead. This more clearly expresses the1158 /// intent that the item is only visible within its own crate.1159 ///1160 /// This lint is "allow" by default because it will trigger for a large amount of existing Rust code.1161 /// Eventually it is desired for this to become warn-by-default.1162 ///1163 /// [`unnameable_types`]: #unnameable-types1164 pub UNREACHABLE_PUB,1165 Allow,1166 "`pub` items not reachable from crate root"1167}11681169declare_lint_pass!(1170 /// Lint for items marked `pub` that aren't reachable from other crates.1171 UnreachablePub => [UNREACHABLE_PUB]1172);11731174impl UnreachablePub {1175 fn perform_lint(1176 &self,1177 cx: &LateContext<'_>,1178 what: &str,1179 def_id: LocalDefId,1180 vis_span: Span,1181 exportable: bool,1182 ) {1183 let mut applicability = Applicability::MachineApplicable;1184 if cx.tcx.visibility(def_id).is_public() && !cx.effective_visibilities.is_reachable(def_id)1185 {1186 // prefer suggesting `pub(super)` instead of `pub(crate)` when possible,1187 // except when `pub(super) == pub(crate)`1188 let new_vis = if let Some(ty::Visibility::Restricted(restricted_did)) =1189 cx.effective_visibilities.effective_vis(def_id).map(|effective_vis| {1190 effective_vis.at_level(rustc_middle::middle::privacy::Level::Reachable)1191 })1192 && let parent_parent = cx1193 .tcx1194 .parent_module_from_def_id(cx.tcx.parent_module_from_def_id(def_id).into())1195 && *restricted_did == parent_parent.to_local_def_id()1196 && !restricted_did.to_def_id().is_crate_root()1197 {1198 "pub(super)"1199 } else {1200 "pub(crate)"1201 };12021203 if vis_span.from_expansion() {1204 applicability = Applicability::MaybeIncorrect;1205 }1206 let def_span = cx.tcx.def_span(def_id);1207 cx.emit_span_lint(1208 UNREACHABLE_PUB,1209 def_span,1210 BuiltinUnreachablePub {1211 what,1212 new_vis,1213 suggestion: (vis_span, applicability),1214 help: exportable,1215 },1216 );1217 }1218 }1219}12201221impl<'tcx> LateLintPass<'tcx> for UnreachablePub {1222 fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {1223 // Do not warn for fake `use` statements.1224 if let hir::ItemKind::Use(_, hir::UseKind::ListStem) = &item.kind {1225 return;1226 }1227 self.perform_lint(cx, "item", item.owner_id.def_id, item.vis_span, true);1228 }12291230 fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'tcx>) {1231 self.perform_lint(cx, "item", foreign_item.owner_id.def_id, foreign_item.vis_span, true);1232 }12331234 fn check_field_def(&mut self, _cx: &LateContext<'_>, _field: &hir::FieldDef<'_>) {1235 // - If an ADT definition is reported then we don't need to check fields1236 // (as it would add unnecessary complexity to the source code, the struct1237 // definition is in the immediate proximity to give the "real" visibility).1238 // - If an ADT is not reported because it's not `pub` - we don't need to1239 // check fields.1240 // - If an ADT is not reported because it's reachable - we also don't need1241 // to check fields because then they are reachable by construction if they1242 // are pub.1243 //1244 // Therefore in no case we check the fields.1245 //1246 // cf. https://github.com/rust-lang/rust/pull/126013#issuecomment-21528392051247 // cf. https://github.com/rust-lang/rust/pull/126040#issuecomment-21529445061248 }12491250 fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {1251 if let ImplItemImplKind::Inherent { vis_span } = impl_item.impl_kind {1252 self.perform_lint(cx, "item", impl_item.owner_id.def_id, vis_span, false);1253 }1254 }1255}12561257declare_lint! {1258 /// The `type_alias_bounds` lint detects bounds in type aliases.1259 ///1260 /// ### Example1261 ///1262 /// ```rust1263 /// type SendVec<T: Send> = Vec<T>;1264 /// ```1265 ///1266 /// {{produces}}1267 ///1268 /// ### Explanation1269 ///1270 /// Trait and lifetime bounds on generic parameters and in where clauses of1271 /// type aliases are not checked at usage sites of the type alias. Moreover,1272 /// they are not thoroughly checked for correctness at their definition site1273 /// either similar to the aliased type.1274 ///1275 /// This is a known limitation of the type checker that may be lifted in a1276 /// future edition. Permitting such bounds in light of this was unintentional.1277 ///1278 /// While these bounds may have secondary effects such as enabling the use of1279 /// "shorthand" associated type paths[^1] and affecting the default trait1280 /// object lifetime[^2] of trait object types passed to the type alias, this1281 /// should not have been allowed until the aforementioned restrictions of the1282 /// type checker have been lifted.1283 ///1284 /// Using such bounds is highly discouraged as they are actively misleading.1285 ///1286 /// [^1]: I.e., paths of the form `T::Assoc` where `T` is a type parameter1287 /// bounded by trait `Trait` which defines an associated type called `Assoc`1288 /// as opposed to a fully qualified path of the form `<T as Trait>::Assoc`.1289 /// [^2]: <https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes>1290 TYPE_ALIAS_BOUNDS,1291 Warn,1292 "bounds in type aliases are not enforced"1293}12941295declare_lint_pass!(TypeAliasBounds => [TYPE_ALIAS_BOUNDS]);12961297impl TypeAliasBounds {1298 pub(crate) fn affects_object_lifetime_defaults(pred: &hir::WherePredicate<'_>) -> bool {1299 // Bounds of the form `T: 'a` with `T` type param affect object lifetime defaults.1300 if let hir::WherePredicateKind::BoundPredicate(pred) = pred.kind1301 && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_)))1302 && pred.bound_generic_params.is_empty() // indeed, even if absent from the RHS1303 && pred.bounded_ty.as_generic_param().is_some()1304 {1305 return true;1306 }1307 false1308 }1309}13101311impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {1312 fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {1313 let hir::ItemKind::TyAlias(_, generics, hir_ty) = item.kind else { return };13141315 // There must not be a where clause.1316 if generics.predicates.is_empty() {1317 return;1318 }13191320 // Bounds of lazy type aliases and TAITs are respected.1321 if cx.tcx.type_alias_is_lazy(item.owner_id) {1322 return;1323 }13241325 // FIXME(generic_const_exprs): Revisit this before stabilization.1326 // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`.1327 let ty = cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip();1328 if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION)1329 && cx.tcx.features().generic_const_exprs()1330 {1331 return;1332 }13331334 // NOTE(inherent_associated_types): While we currently do take some bounds in type1335 // aliases into consideration during IAT *selection*, we don't perform full use+def1336 // site wfchecking for such type aliases. Therefore TAB should still trigger.1337 // See also `tests/ui/associated-inherent-types/type-alias-bounds.rs`.13381339 let mut where_spans = Vec::new();1340 let mut inline_spans = Vec::new();1341 let mut inline_sugg = Vec::new();13421343 for p in generics.predicates {1344 let span = p.span;1345 if p.kind.in_where_clause() {1346 where_spans.push(span);1347 } else {1348 for b in p.kind.bounds() {1349 inline_spans.push(b.span());1350 }1351 inline_sugg.push((span, String::new()));1352 }1353 }13541355 let mut ty = Some(hir_ty);1356 let enable_feat_help = cx.tcx.sess.is_nightly_build();13571358 if let [.., label_sp] = *where_spans {1359 cx.emit_span_lint(1360 TYPE_ALIAS_BOUNDS,1361 where_spans,1362 BuiltinTypeAliasBounds {1363 in_where_clause: true,1364 label: label_sp,1365 enable_feat_help,1366 suggestions: vec![(generics.where_clause_span, String::new())],1367 preds: generics.predicates,1368 ty: ty.take(),1369 },1370 );1371 }1372 if let [.., label_sp] = *inline_spans {1373 cx.emit_span_lint(1374 TYPE_ALIAS_BOUNDS,1375 inline_spans,1376 BuiltinTypeAliasBounds {1377 in_where_clause: false,1378 label: label_sp,1379 enable_feat_help,1380 suggestions: inline_sugg,1381 preds: generics.predicates,1382 ty,1383 },1384 );1385 }1386 }1387}13881389pub(crate) struct ShorthandAssocTyCollector {1390 pub(crate) qselves: Vec<Span>,1391}13921393impl hir::intravisit::Visitor<'_> for ShorthandAssocTyCollector {1394 fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, _: Span) {1395 // Look for "type-parameter shorthand-associated-types". I.e., paths of the1396 // form `T::Assoc` with `T` type param. These are reliant on trait bounds.1397 if let hir::QPath::TypeRelative(qself, _) = qpath1398 && qself.as_generic_param().is_some()1399 {1400 self.qselves.push(qself.span);1401 }1402 hir::intravisit::walk_qpath(self, qpath, id)1403 }1404}14051406declare_lint! {1407 /// The `trivial_bounds` lint detects trait bounds that don't depend on1408 /// any type parameters.1409 ///1410 /// ### Example1411 ///1412 /// ```rust1413 /// #![feature(trivial_bounds)]1414 /// pub struct A where i32: Copy;1415 /// ```1416 ///1417 /// {{produces}}1418 ///1419 /// ### Explanation1420 ///1421 /// Usually you would not write a trait bound that you know is always1422 /// true, or never true. However, when using macros, the macro may not1423 /// know whether or not the constraint would hold or not at the time when1424 /// generating the code. Currently, the compiler does not alert you if the1425 /// constraint is always true, and generates an error if it is never true.1426 /// The `trivial_bounds` feature changes this to be a warning in both1427 /// cases, giving macros more freedom and flexibility to generate code,1428 /// while still providing a signal when writing non-macro code that1429 /// something is amiss.1430 ///1431 /// See [RFC 2056] for more details. This feature is currently only1432 /// available on the nightly channel, see [tracking issue #48214].1433 ///1434 /// [RFC 2056]: https://github.com/rust-lang/rfcs/blob/master/text/2056-allow-trivial-where-clause-constraints.md1435 /// [tracking issue #48214]: https://github.com/rust-lang/rust/issues/482141436 TRIVIAL_BOUNDS,1437 Warn,1438 "these bounds don't depend on an type parameters"1439}14401441declare_lint_pass!(1442 /// Lint for trait and lifetime bounds that don't depend on type parameters1443 /// which either do nothing, or stop the item from being used.1444 TrivialConstraints => [TRIVIAL_BOUNDS]1445);14461447impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {1448 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {1449 use rustc_middle::ty::ClauseKind;14501451 if cx.tcx.features().trivial_bounds() {1452 let predicates = cx.tcx.predicates_of(item.owner_id);1453 for &(predicate, span) in predicates.predicates {1454 let predicate_kind_name = match predicate.kind().skip_binder() {1455 ClauseKind::Trait(..) => "trait",1456 ClauseKind::TypeOutlives(..) | ClauseKind::RegionOutlives(..) => "lifetime",14571458 ClauseKind::UnstableFeature(_)1459 // `ConstArgHasType` is never global as `ct` is always a param1460 | ClauseKind::ConstArgHasType(..)1461 // Ignore projections, as they can only be global1462 // if the trait bound is global1463 | ClauseKind::Projection(..)1464 // Ignore bounds that a user can't type1465 | ClauseKind::WellFormed(..)1466 // FIXME(generic_const_exprs): `ConstEvaluatable` can be written1467 | ClauseKind::ConstEvaluatable(..)1468 // Users don't write this directly, only via another trait ref.1469 | ty::ClauseKind::HostEffect(..) => continue,1470 };1471 if predicate.is_global() {1472 cx.emit_span_lint(1473 TRIVIAL_BOUNDS,1474 span,1475 BuiltinTrivialBounds { predicate_kind_name, predicate },1476 );1477 }1478 }1479 }1480 }1481}14821483declare_lint! {1484 /// The `double_negations` lint detects expressions of the form `--x`.1485 ///1486 /// ### Example1487 ///1488 /// ```rust1489 /// fn main() {1490 /// let x = 1;1491 /// let _b = --x;1492 /// }1493 /// ```1494 ///1495 /// {{produces}}1496 ///1497 /// ### Explanation1498 ///1499 /// Negating something twice is usually the same as not negating it at all.1500 /// However, a double negation in Rust can easily be confused with the1501 /// prefix decrement operator that exists in many languages derived from C.1502 /// Use `-(-x)` if you really wanted to negate the value twice.1503 ///1504 /// To decrement a value, use `x -= 1` instead.1505 pub DOUBLE_NEGATIONS,1506 Warn,1507 "detects expressions of the form `--x`"1508}15091510declare_lint_pass!(1511 /// Lint for expressions of the form `--x` that can be confused with C's1512 /// prefix decrement operator.1513 DoubleNegations => [DOUBLE_NEGATIONS]1514);15151516impl EarlyLintPass for DoubleNegations {1517 #[inline]1518 fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {1519 // only lint on the innermost `--` in a chain of `-` operators,1520 // even if there are 3 or more negations1521 if let ExprKind::Unary(UnOp::Neg, ref inner) = expr.kind1522 && let ExprKind::Unary(UnOp::Neg, ref inner2) = inner.kind1523 && !matches!(inner2.kind, ExprKind::Unary(UnOp::Neg, _))1524 // Don't lint if this jumps macro expansion boundary (Issue #143980)1525 && expr.span.eq_ctxt(inner.span)1526 {1527 cx.emit_span_lint(1528 DOUBLE_NEGATIONS,1529 expr.span,1530 BuiltinDoubleNegations {1531 add_parens: BuiltinDoubleNegationsAddParens {1532 start_span: inner.span.shrink_to_lo(),1533 end_span: inner.span.shrink_to_hi(),1534 },1535 },1536 );1537 }1538 }1539}15401541pub mod soft {1542 use super::*;15431544 pub fn lint_vec() -> crate::LintVec {1545 vec![1546 WHILE_TRUE,1547 NON_SHORTHAND_FIELD_PATTERNS,1548 UNSAFE_CODE,1549 MISSING_DOCS,1550 MISSING_COPY_IMPLEMENTATIONS,1551 MISSING_DEBUG_IMPLEMENTATIONS,1552 ANONYMOUS_PARAMETERS,1553 UNUSED_DOC_COMMENTS,1554 NO_MANGLE_CONST_ITEMS,1555 NO_MANGLE_GENERIC_ITEMS,1556 MUTABLE_TRANSMUTES,1557 UNSTABLE_FEATURES,1558 UNREACHABLE_PUB,1559 TYPE_ALIAS_BOUNDS,1560 TRIVIAL_BOUNDS,1561 DOUBLE_NEGATIONS,1562 ]1563 }1564}15651566declare_lint! {1567 /// The `ellipsis_inclusive_range_patterns` lint detects the [`...` range1568 /// pattern], which is deprecated.1569 ///1570 /// [`...` range pattern]: https://doc.rust-lang.org/reference/patterns.html#range-patterns1571 ///1572 /// ### Example1573 ///1574 /// ```rust,edition20181575 /// let x = 123;1576 /// match x {1577 /// 0...100 => {}1578 /// _ => {}1579 /// }1580 /// ```1581 ///1582 /// {{produces}}1583 ///1584 /// ### Explanation1585 ///1586 /// The `...` range pattern syntax was changed to `..=` to avoid potential1587 /// confusion with the [`..` range expression]. Use the new form instead.1588 ///1589 /// [`..` range expression]: https://doc.rust-lang.org/reference/expressions/range-expr.html1590 pub ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,1591 Warn,1592 "`...` range patterns are deprecated",1593 @future_incompatible = FutureIncompatibleInfo {1594 reason: fcw!(EditionError 2021 "warnings-promoted-to-error"),1595 };1596}15971598#[derive(Default)]1599pub struct EllipsisInclusiveRangePatterns {1600 /// If `Some(_)`, suppress all subsequent pattern1601 /// warnings for better diagnostics.1602 node_id: Option<ast::NodeId>,1603}16041605impl_lint_pass!(EllipsisInclusiveRangePatterns => [ELLIPSIS_INCLUSIVE_RANGE_PATTERNS]);16061607impl EarlyLintPass for EllipsisInclusiveRangePatterns {1608 fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &ast::Pat) {1609 if self.node_id.is_some() {1610 // Don't recursively warn about patterns inside range endpoints.1611 return;1612 }16131614 use self::ast::PatKind;1615 use self::ast::RangeSyntax::DotDotDot;16161617 /// If `pat` is a `...` pattern, return the start and end of the range, as well as the span1618 /// corresponding to the ellipsis.1619 fn matches_ellipsis_pat(pat: &ast::Pat) -> Option<(Option<&Expr>, &Expr, Span)> {1620 match &pat.kind {1621 PatKind::Range(1622 a,1623 Some(b),1624 Spanned { span, node: RangeEnd::Included(DotDotDot) },1625 ) => Some((a.as_deref(), b, *span)),1626 _ => None,1627 }1628 }16291630 let (parentheses, endpoints) = match &pat.kind {1631 PatKind::Ref(subpat, _, _) => (true, matches_ellipsis_pat(subpat)),1632 _ => (false, matches_ellipsis_pat(pat)),1633 };16341635 if let Some((start, end, join)) = endpoints {1636 if parentheses {1637 self.node_id = Some(pat.id);1638 let end = expr_to_string(end);1639 let replace = match start {1640 Some(start) => format!("&({}..={})", expr_to_string(start), end),1641 None => format!("&(..={end})"),1642 };1643 if join.edition() >= Edition::Edition2021 {1644 cx.sess().dcx().emit_err(BuiltinEllipsisInclusiveRangePatterns {1645 span: pat.span,1646 suggestion: pat.span,1647 replace,1648 });1649 } else {1650 cx.emit_span_lint(1651 ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,1652 pat.span,1653 BuiltinEllipsisInclusiveRangePatternsLint::Parenthesise {1654 suggestion: pat.span,1655 replace,1656 },1657 );1658 }1659 } else {1660 let replace = "..=";1661 if join.edition() >= Edition::Edition2021 {1662 cx.sess().dcx().emit_err(BuiltinEllipsisInclusiveRangePatterns {1663 span: pat.span,1664 suggestion: join,1665 replace: replace.to_string(),1666 });1667 } else {1668 cx.emit_span_lint(1669 ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,1670 join,1671 BuiltinEllipsisInclusiveRangePatternsLint::NonParenthesise {1672 suggestion: join,1673 },1674 );1675 }1676 };1677 }1678 }16791680 fn check_pat_post(&mut self, _cx: &EarlyContext<'_>, pat: &ast::Pat) {1681 if let Some(node_id) = self.node_id {1682 if pat.id == node_id {1683 self.node_id = None1684 }1685 }1686 }1687}16881689declare_lint! {1690 /// The `keyword_idents_2018` lint detects edition keywords being used as an1691 /// identifier.1692 ///1693 /// ### Example1694 ///1695 /// ```rust,edition2015,compile_fail1696 /// #![deny(keyword_idents_2018)]1697 /// // edition 20151698 /// fn dyn() {}1699 /// ```1700 ///1701 /// {{produces}}1702 ///1703 /// ### Explanation1704 ///1705 /// Rust [editions] allow the language to evolve without breaking1706 /// backwards compatibility. This lint catches code that uses new keywords1707 /// that are added to the language that are used as identifiers (such as a1708 /// variable name, function name, etc.). If you switch the compiler to a1709 /// new edition without updating the code, then it will fail to compile if1710 /// you are using a new keyword as an identifier.1711 ///1712 /// You can manually change the identifiers to a non-keyword, or use a1713 /// [raw identifier], for example `r#dyn`, to transition to a new edition.1714 ///1715 /// This lint solves the problem automatically. It is "allow" by default1716 /// because the code is perfectly valid in older editions. The [`cargo1717 /// fix`] tool with the `--edition` flag will switch this lint to "warn"1718 /// and automatically apply the suggested fix from the compiler (which is1719 /// to use a raw identifier). This provides a completely automated way to1720 /// update old code for a new edition.1721 ///1722 /// [editions]: https://doc.rust-lang.org/edition-guide/1723 /// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html1724 /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html1725 pub KEYWORD_IDENTS_2018,1726 Allow,1727 "detects edition keywords being used as an identifier",1728 @future_incompatible = FutureIncompatibleInfo {1729 reason: fcw!(EditionError 2018 "new-keywords"),1730 };1731}17321733declare_lint! {1734 /// The `keyword_idents_2024` lint detects edition keywords being used as an1735 /// identifier.1736 ///1737 /// ### Example1738 ///1739 /// ```rust,edition2015,compile_fail1740 /// #![deny(keyword_idents_2024)]1741 /// // edition 20151742 /// fn gen() {}1743 /// ```1744 ///1745 /// {{produces}}1746 ///1747 /// ### Explanation1748 ///1749 /// Rust [editions] allow the language to evolve without breaking1750 /// backwards compatibility. This lint catches code that uses new keywords1751 /// that are added to the language that are used as identifiers (such as a1752 /// variable name, function name, etc.). If you switch the compiler to a1753 /// new edition without updating the code, then it will fail to compile if1754 /// you are using a new keyword as an identifier.1755 ///1756 /// You can manually change the identifiers to a non-keyword, or use a1757 /// [raw identifier], for example `r#gen`, to transition to a new edition.1758 ///1759 /// This lint solves the problem automatically. It is "allow" by default1760 /// because the code is perfectly valid in older editions. The [`cargo1761 /// fix`] tool with the `--edition` flag will switch this lint to "warn"1762 /// and automatically apply the suggested fix from the compiler (which is1763 /// to use a raw identifier). This provides a completely automated way to1764 /// update old code for a new edition.1765 ///1766 /// [editions]: https://doc.rust-lang.org/edition-guide/1767 /// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html1768 /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html1769 pub KEYWORD_IDENTS_2024,1770 Allow,1771 "detects edition keywords being used as an identifier",1772 @future_incompatible = FutureIncompatibleInfo {1773 reason: fcw!(EditionError 2024 "gen-keyword"),1774 };1775}17761777declare_lint_pass!(1778 /// Check for uses of edition keywords used as an identifier.1779 KeywordIdents => [KEYWORD_IDENTS_2018, KEYWORD_IDENTS_2024]1780);17811782struct UnderMacro(bool);17831784impl KeywordIdents {1785 fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: &TokenStream) {1786 // Check if the preceding token is `$`, because we want to allow `$async`, etc.1787 let mut prev_dollar = false;1788 for tt in tokens.iter() {1789 match tt {1790 // Only report non-raw idents.1791 TokenTree::Token(token, _) => {1792 if let Some((ident, token::IdentIsRaw::No)) = token.ident() {1793 if !prev_dollar {1794 self.check_ident_token(cx, UnderMacro(true), ident, "");1795 }1796 } else if let Some((ident, token::IdentIsRaw::No)) = token.lifetime() {1797 self.check_ident_token(1798 cx,1799 UnderMacro(true),1800 ident.without_first_quote(),1801 "'",1802 );1803 } else if token.kind == TokenKind::Dollar {1804 prev_dollar = true;1805 continue;1806 }1807 }1808 TokenTree::Delimited(.., tts) => self.check_tokens(cx, tts),1809 }1810 prev_dollar = false;1811 }1812 }18131814 fn check_ident_token(1815 &mut self,1816 cx: &EarlyContext<'_>,1817 UnderMacro(under_macro): UnderMacro,1818 ident: Ident,1819 prefix: &'static str,1820 ) {1821 let (lint, edition) = match ident.name {1822 kw::Async | kw::Await | kw::Try => (KEYWORD_IDENTS_2018, Edition::Edition2018),18231824 // rust-lang/rust#56327: Conservatively do not1825 // attempt to report occurrences of `dyn` within1826 // macro definitions or invocations, because `dyn`1827 // can legitimately occur as a contextual keyword1828 // in 2015 code denoting its 2018 meaning, and we1829 // do not want rustfix to inject bugs into working1830 // code by rewriting such occurrences.1831 //1832 // But if we see `dyn` outside of a macro, we know1833 // its precise role in the parsed AST and thus are1834 // assured this is truly an attempt to use it as1835 // an identifier.1836 kw::Dyn if !under_macro => (KEYWORD_IDENTS_2018, Edition::Edition2018),18371838 kw::Gen => (KEYWORD_IDENTS_2024, Edition::Edition2024),18391840 _ => return,1841 };18421843 // Don't lint `r#foo`.1844 if ident.span.edition() >= edition1845 || cx.sess().psess.raw_identifier_spans.contains(ident.span)1846 {1847 return;1848 }18491850 cx.emit_span_lint(1851 lint,1852 ident.span,1853 BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span, prefix },1854 );1855 }1856}18571858impl EarlyLintPass for KeywordIdents {1859 fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef) {1860 self.check_tokens(cx, &mac_def.body.tokens);1861 }1862 fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) {1863 self.check_tokens(cx, &mac.args.tokens);1864 }1865 fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: &Ident) {1866 if ident.name.as_str().starts_with('\'') {1867 self.check_ident_token(cx, UnderMacro(false), ident.without_first_quote(), "'");1868 } else {1869 self.check_ident_token(cx, UnderMacro(false), *ident, "");1870 }1871 }1872}18731874declare_lint_pass!(ExplicitOutlivesRequirements => [EXPLICIT_OUTLIVES_REQUIREMENTS]);18751876impl ExplicitOutlivesRequirements {1877 fn lifetimes_outliving_lifetime<'tcx>(1878 tcx: TyCtxt<'tcx>,1879 inferred_outlives: impl Iterator<Item = &'tcx (ty::Clause<'tcx>, Span)>,1880 item: LocalDefId,1881 lifetime: LocalDefId,1882 ) -> Vec<ty::Region<'tcx>> {1883 let item_generics = tcx.generics_of(item);18841885 inferred_outlives1886 .filter_map(|(clause, _)| match clause.kind().skip_binder() {1887 ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match a.kind() {1888 ty::ReEarlyParam(ebr)1889 if item_generics.region_param(ebr, tcx).def_id == lifetime.to_def_id() =>1890 {1891 Some(b)1892 }1893 _ => None,1894 },1895 _ => None,1896 })1897 .collect()1898 }18991900 fn lifetimes_outliving_type<'tcx>(1901 inferred_outlives: impl Iterator<Item = &'tcx (ty::Clause<'tcx>, Span)>,1902 index: u32,1903 ) -> Vec<ty::Region<'tcx>> {1904 inferred_outlives1905 .filter_map(|(clause, _)| match clause.kind().skip_binder() {1906 ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => {1907 a.is_param(index).then_some(b)1908 }1909 _ => None,1910 })1911 .collect()1912 }19131914 fn collect_outlives_bound_spans<'tcx>(1915 &self,1916 tcx: TyCtxt<'tcx>,1917 bounds: &hir::GenericBounds<'_>,1918 inferred_outlives: &[ty::Region<'tcx>],1919 predicate_span: Span,1920 item: DefId,1921 ) -> Vec<(usize, Span)> {1922 use rustc_middle::middle::resolve_bound_vars::ResolvedArg;19231924 let item_generics = tcx.generics_of(item);19251926 bounds1927 .iter()1928 .enumerate()1929 .filter_map(|(i, bound)| {1930 let hir::GenericBound::Outlives(lifetime) = bound else {1931 return None;1932 };19331934 let is_inferred = match tcx.named_bound_var(lifetime.hir_id) {1935 Some(ResolvedArg::EarlyBound(def_id)) => inferred_outlives1936 .iter()1937 .any(|r| matches!(r.kind(), ty::ReEarlyParam(ebr) if { item_generics.region_param(ebr, tcx).def_id == def_id.to_def_id() })),1938 _ => false,1939 };19401941 if !is_inferred {1942 return None;1943 }19441945 let span = bound.span().find_ancestor_inside(predicate_span)?;1946 if span.in_external_macro(tcx.sess.source_map()) {1947 return None;1948 }19491950 Some((i, span))1951 })1952 .collect()1953 }19541955 fn consolidate_outlives_bound_spans(1956 &self,1957 lo: Span,1958 bounds: &hir::GenericBounds<'_>,1959 bound_spans: Vec<(usize, Span)>,1960 ) -> Vec<Span> {1961 if bounds.is_empty() {1962 return Vec::new();1963 }1964 if bound_spans.len() == bounds.len() {1965 let (_, last_bound_span) = bound_spans[bound_spans.len() - 1];1966 // If all bounds are inferable, we want to delete the colon, so1967 // start from just after the parameter (span passed as argument)1968 vec![lo.to(last_bound_span)]1969 } else {1970 let mut merged = Vec::new();1971 let mut last_merged_i = None;19721973 let mut from_start = true;1974 for (i, bound_span) in bound_spans {1975 match last_merged_i {1976 // If the first bound is inferable, our span should also eat the leading `+`.1977 None if i == 0 => {1978 merged.push(bound_span.to(bounds[1].span().shrink_to_lo()));1979 last_merged_i = Some(0);1980 }1981 // If consecutive bounds are inferable, merge their spans1982 Some(h) if i == h + 1 => {1983 if let Some(tail) = merged.last_mut() {1984 // Also eat the trailing `+` if the first1985 // more-than-one bound is inferable1986 let to_span = if from_start && i < bounds.len() {1987 bounds[i + 1].span().shrink_to_lo()1988 } else {1989 bound_span1990 };1991 *tail = tail.to(to_span);1992 last_merged_i = Some(i);1993 } else {1994 bug!("another bound-span visited earlier");1995 }1996 }1997 _ => {1998 // When we find a non-inferable bound, subsequent inferable bounds1999 // won't be consecutive from the start (and we'll eat the leading2000 // `+` rather than the trailing one)
Findings
✓ No findings reported for this file.