diff options
Diffstat (limited to 'compiler/rustc_lint/src/builtin.rs')
| -rw-r--r-- | compiler/rustc_lint/src/builtin.rs | 176 |
1 files changed, 117 insertions, 59 deletions
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 8b974a461d4..ba42eae3441 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -30,16 +30,17 @@ use crate::{ BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures, BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, - BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, - BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds, - BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause, - BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, + BuiltinMutablesTransmutes, BuiltinNamedAsmLabel, BuiltinNoMangleGeneric, + BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, + BuiltinTypeAliasGenericBounds, BuiltinTypeAliasGenericBoundsSuggestion, + BuiltinTypeAliasWhereClause, BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, BuiltinWhileTrue, SuggestChangingAssocTypes, }, EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, }; +use ast::token::TokenKind; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{self as ast, *}; @@ -51,14 +52,16 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::intravisit::FnKind as HirFnKind; use rustc_hir::{Body, FnDecl, GenericParamKind, Node, PatKind, PredicateOrigin}; +use rustc_middle::bug; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::GenericArgKind; -use rustc_middle::ty::ToPredicate; use rustc_middle::ty::TypeVisitableExt; +use rustc_middle::ty::Upcast; use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; -use rustc_session::lint::{BuiltinLintDiag, FutureIncompatibilityReason}; +use rustc_session::lint::FutureIncompatibilityReason; +use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -67,6 +70,7 @@ use rustc_target::abi::Abi; use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{self, misc::type_allowed_to_implement_copy}; +use tracing::debug; use crate::nonstandard_style::{method_context, MethodLateContext}; @@ -357,11 +361,11 @@ impl EarlyLintPass for UnsafeCode { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { match it.kind { - ast::ItemKind::Trait(box ast::Trait { unsafety: ast::Unsafe::Yes(_), .. }) => { + ast::ItemKind::Trait(box ast::Trait { safety: ast::Safety::Unsafe(_), .. }) => { self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeTrait); } - ast::ItemKind::Impl(box ast::Impl { unsafety: ast::Unsafe::Yes(_), .. }) => { + ast::ItemKind::Impl(box ast::Impl { safety: ast::Safety::Unsafe(_), .. }) => { self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeImpl); } @@ -416,7 +420,7 @@ impl EarlyLintPass for UnsafeCode { if let FnKind::Fn( ctxt, _, - ast::FnSig { header: ast::FnHeader { unsafety: ast::Unsafe::Yes(_), .. }, .. }, + ast::FnSig { header: ast::FnHeader { safety: ast::Safety::Unsafe(_), .. }, .. }, _, _, body, @@ -671,11 +675,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { return; } } - let param_env = ty::ParamEnv::empty(); - if ty.is_copy_modulo_regions(cx.tcx, param_env) { + if ty.is_copy_modulo_regions(cx.tcx, cx.param_env) { return; } - if type_implements_negative_copy_modulo_regions(cx.tcx, ty, param_env) { + if type_implements_negative_copy_modulo_regions(cx.tcx, ty, cx.param_env) { return; } if def.is_variant_list_non_exhaustive() @@ -691,7 +694,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { .tcx .infer_ctxt() .build() - .type_implements_trait(iter_trait, [ty], param_env) + .type_implements_trait(iter_trait, [ty], cx.param_env) .must_apply_modulo_regions() { return; @@ -708,7 +711,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { if type_allowed_to_implement_copy( cx.tcx, - param_env, + cx.param_env, ty, traits::ObligationCause::misc(item.span, item.owner_id.def_id), ) @@ -731,7 +734,7 @@ fn type_implements_negative_copy_modulo_regions<'tcx>( cause: traits::ObligationCause::dummy(), param_env, recursion_depth: 0, - predicate: ty::Binder::dummy(pred).to_predicate(tcx), + predicate: pred.upcast(tcx), }; tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation) @@ -1769,13 +1772,13 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { } declare_lint! { - /// The `keyword_idents` lint detects edition keywords being used as an + /// The `keyword_idents_2018` lint detects edition keywords being used as an /// identifier. /// /// ### Example /// /// ```rust,edition2015,compile_fail - /// #![deny(keyword_idents)] + /// #![deny(keyword_idents_2018)] /// // edition 2015 /// fn dyn() {} /// ``` @@ -1804,7 +1807,7 @@ declare_lint! { /// [editions]: https://doc.rust-lang.org/edition-guide/ /// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html - pub KEYWORD_IDENTS, + pub KEYWORD_IDENTS_2018, Allow, "detects edition keywords being used as an identifier", @future_incompatible = FutureIncompatibleInfo { @@ -1813,25 +1816,78 @@ declare_lint! { }; } +declare_lint! { + /// The `keyword_idents_2024` lint detects edition keywords being used as an + /// identifier. + /// + /// ### Example + /// + /// ```rust,edition2015,compile_fail + /// #![deny(keyword_idents_2024)] + /// // edition 2015 + /// fn gen() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Rust [editions] allow the language to evolve without breaking + /// backwards compatibility. This lint catches code that uses new keywords + /// that are added to the language that are used as identifiers (such as a + /// variable name, function name, etc.). If you switch the compiler to a + /// new edition without updating the code, then it will fail to compile if + /// you are using a new keyword as an identifier. + /// + /// You can manually change the identifiers to a non-keyword, or use a + /// [raw identifier], for example `r#gen`, to transition to a new edition. + /// + /// This lint solves the problem automatically. It is "allow" by default + /// because the code is perfectly valid in older editions. The [`cargo + /// fix`] tool with the `--edition` flag will switch this lint to "warn" + /// and automatically apply the suggested fix from the compiler (which is + /// to use a raw identifier). This provides a completely automated way to + /// update old code for a new edition. + /// + /// [editions]: https://doc.rust-lang.org/edition-guide/ + /// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html + /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html + pub KEYWORD_IDENTS_2024, + Allow, + "detects edition keywords being used as an identifier", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), + reference: "issue #49716 <https://github.com/rust-lang/rust/issues/49716>", + }; +} + declare_lint_pass!( /// Check for uses of edition keywords used as an identifier. - KeywordIdents => [KEYWORD_IDENTS] + KeywordIdents => [KEYWORD_IDENTS_2018, KEYWORD_IDENTS_2024] ); struct UnderMacro(bool); impl KeywordIdents { fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: &TokenStream) { + // Check if the preceding token is `$`, because we want to allow `$async`, etc. + let mut prev_dollar = false; for tt in tokens.trees() { match tt { // Only report non-raw idents. TokenTree::Token(token, _) => { if let Some((ident, token::IdentIsRaw::No)) = token.ident() { - self.check_ident_token(cx, UnderMacro(true), ident); + if !prev_dollar { + self.check_ident_token(cx, UnderMacro(true), ident); + } + } else if token.kind == TokenKind::Dollar { + prev_dollar = true; + continue; } } TokenTree::Delimited(.., tts) => self.check_tokens(cx, tts), } + prev_dollar = false; } } @@ -1841,42 +1897,39 @@ impl KeywordIdents { UnderMacro(under_macro): UnderMacro, ident: Ident, ) { - let next_edition = match cx.sess().edition() { - Edition::Edition2015 => { - match ident.name { - kw::Async | kw::Await | kw::Try => Edition::Edition2018, - - // rust-lang/rust#56327: Conservatively do not - // attempt to report occurrences of `dyn` within - // macro definitions or invocations, because `dyn` - // can legitimately occur as a contextual keyword - // in 2015 code denoting its 2018 meaning, and we - // do not want rustfix to inject bugs into working - // code by rewriting such occurrences. - // - // But if we see `dyn` outside of a macro, we know - // its precise role in the parsed AST and thus are - // assured this is truly an attempt to use it as - // an identifier. - kw::Dyn if !under_macro => Edition::Edition2018, - - _ => return, - } - } + let (lint, edition) = match ident.name { + kw::Async | kw::Await | kw::Try => (KEYWORD_IDENTS_2018, Edition::Edition2018), + + // rust-lang/rust#56327: Conservatively do not + // attempt to report occurrences of `dyn` within + // macro definitions or invocations, because `dyn` + // can legitimately occur as a contextual keyword + // in 2015 code denoting its 2018 meaning, and we + // do not want rustfix to inject bugs into working + // code by rewriting such occurrences. + // + // But if we see `dyn` outside of a macro, we know + // its precise role in the parsed AST and thus are + // assured this is truly an attempt to use it as + // an identifier. + kw::Dyn if !under_macro => (KEYWORD_IDENTS_2018, Edition::Edition2018), + + kw::Gen => (KEYWORD_IDENTS_2024, Edition::Edition2024), - // There are no new keywords yet for the 2018 edition and beyond. _ => return, }; // Don't lint `r#foo`. - if cx.sess().psess.raw_identifier_spans.contains(ident.span) { + if ident.span.edition() >= edition + || cx.sess().psess.raw_identifier_spans.contains(ident.span) + { return; } cx.emit_span_lint( - KEYWORD_IDENTS, + lint, ident.span, - BuiltinKeywordIdents { kw: ident, next: next_edition, suggestion: ident.span }, + BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span }, ); } } @@ -1897,14 +1950,22 @@ declare_lint_pass!(ExplicitOutlivesRequirements => [EXPLICIT_OUTLIVES_REQUIREMEN impl ExplicitOutlivesRequirements { fn lifetimes_outliving_lifetime<'tcx>( + tcx: TyCtxt<'tcx>, inferred_outlives: &'tcx [(ty::Clause<'tcx>, Span)], - def_id: DefId, + item: DefId, + lifetime: DefId, ) -> Vec<ty::Region<'tcx>> { + let item_generics = tcx.generics_of(item); + inferred_outlives .iter() .filter_map(|(clause, _)| match clause.kind().skip_binder() { ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a { - ty::ReEarlyParam(ebr) if ebr.def_id == def_id => Some(b), + ty::ReEarlyParam(ebr) + if item_generics.region_param(ebr, tcx).def_id == lifetime => + { + Some(b) + } _ => None, }, _ => None, @@ -1933,9 +1994,12 @@ impl ExplicitOutlivesRequirements { bounds: &hir::GenericBounds<'_>, inferred_outlives: &[ty::Region<'tcx>], predicate_span: Span, + item: DefId, ) -> Vec<(usize, Span)> { use rustc_middle::middle::resolve_bound_vars::ResolvedArg; + let item_generics = tcx.generics_of(item); + bounds .iter() .enumerate() @@ -1947,7 +2011,7 @@ impl ExplicitOutlivesRequirements { let is_inferred = match tcx.named_bound_var(lifetime.hir_id) { Some(ResolvedArg::EarlyBound(def_id)) => inferred_outlives .iter() - .any(|r| matches!(**r, ty::ReEarlyParam(ebr) if { ebr.def_id == def_id })), + .any(|r| matches!(**r, ty::ReEarlyParam(ebr) if { item_generics.region_param(ebr, tcx).def_id == def_id })), _ => false, }; @@ -2056,7 +2120,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { { ( Self::lifetimes_outliving_lifetime( + cx.tcx, inferred_outlives, + item.owner_id.to_def_id(), region_def_id, ), &predicate.bounds, @@ -2099,6 +2165,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { bounds, &relevant_lifetimes, predicate_span, + item.owner_id.to_def_id(), ); bound_count += bound_spans.len(); @@ -2829,16 +2896,7 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { let target_spans: MultiSpan = if spans.len() > 0 { spans.into() } else { (*template_span).into() }; - cx.span_lint_with_diagnostics( - NAMED_ASM_LABELS, - Some(target_spans), - fluent::lint_builtin_asm_labels, - |_| {}, - BuiltinLintDiag::NamedAsmLabel( - "only local labels of the form `<number>:` should be used in inline asm" - .to_string(), - ), - ); + cx.emit_span_lint(NAMED_ASM_LABELS, target_spans, BuiltinNamedAsmLabel); } } } |
