diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 7b39b953e3be0..fc89029fe1a87 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -234,7 +234,7 @@ impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) { type Error = !; fn typeck_results(&self) -> Self::TypeckResults<'_> { - self.0.maybe_typeck_results().expect("expected typeck results") + self.0.typeck_results() } fn structurally_resolve_type(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 3348b61762c75..83344c33981ca 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -3,7 +3,6 @@ //! See for an //! overview of how lints are implemented. -use std::cell::Cell; use std::slice; use rustc_abi as abi; @@ -468,11 +467,8 @@ pub struct LateContext<'tcx> { /// Current body, or `None` if outside a body. pub enclosing_body: Option, - /// Type-checking results for the current body. Access using the `typeck_results` - /// and `maybe_typeck_results` methods, which handle querying the typeck results on demand. - // FIXME(eddyb) move all the code accessing internal fields like this, - // to this module, to avoid exposing it to lint logic. - pub(super) cached_typeck_results: Cell>>, + /// Type-checking results for the current body. + pub typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>, /// Parameter environment for the item we are in. pub param_env: ty::ParamEnv<'tcx>, @@ -647,24 +643,13 @@ impl<'tcx> LateContext<'tcx> { self.tcx.type_is_use_cloned_modulo_regions(self.typing_env(), ty) } - /// Gets the type-checking results for the current body, - /// or `None` if outside a body. - pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> { - self.cached_typeck_results.get().or_else(|| { - self.enclosing_body.map(|body| { - let typeck_results = self.tcx.typeck_body(body); - self.cached_typeck_results.set(Some(typeck_results)); - typeck_results - }) - }) - } - /// Gets the type-checking results for the current body. /// As this will ICE if called outside bodies, only call when working with /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). + #[inline] #[track_caller] pub fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> { - self.maybe_typeck_results().expect("`LateContext::typeck_results` called outside of body") + self.typeck_results.expect("`LateContext::typeck_results` called outside of body") } /// Returns the final resolution of a `QPath`, or `Res::Err` if unavailable. @@ -674,7 +659,7 @@ impl<'tcx> LateContext<'tcx> { match *qpath { hir::QPath::Resolved(_, path) => path.res, hir::QPath::TypeRelative(..) => self - .maybe_typeck_results() + .typeck_results .filter(|typeck_results| typeck_results.hir_owner == id.owner) .or_else(|| { self.tcx diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index fea6b2ab2f9bd..2acdd1d831d4f 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -4,19 +4,19 @@ //! borrow checking, etc.). These lints have full type information available. use std::any::Any; -use std::cell::Cell; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::par_join; use rustc_hir::def_id::{LocalDefId, LocalModDefId}; -use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit}; +use rustc_hir::{self as hir, AmbigArg, BodyId, HirId, intravisit as hir_visit}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt, TypeckResults}; use rustc_session::Session; use rustc_session::lint::LintPass; use rustc_span::Span; use tracing::debug; +use crate::builtin::MissingDoc; use crate::passes::LateLintPassObject; use crate::{LateContext, LateLintPass, LintStore, is_lint_pass_required}; @@ -34,12 +34,38 @@ macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({ /// Implements the AST traversal for late lint passes. `T` provides the /// `check_*` methods. -struct LateContextAndPass<'tcx, T: LateLintPass<'tcx>> { +struct LateContextAndPass<'tcx, T: LateLintPass<'tcx>, Typeck> { context: LateContext<'tcx>, pass: T, + // See comment on `lint_missing_docs`. + _typeck_body: Typeck, } -impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { +trait TypeckBody { + fn typeck_body<'tcx>( + cx: &mut LateContext<'tcx>, + body: BodyId, + ) -> Option<&'tcx TypeckResults<'tcx>>; +} +impl TypeckBody for () { + fn typeck_body<'tcx>( + cx: &mut LateContext<'tcx>, + body: BodyId, + ) -> Option<&'tcx TypeckResults<'tcx>> { + cx.typeck_results.replace(cx.tcx.typeck_body(body)) + } +} +struct NoTypeckBody; +impl TypeckBody for NoTypeckBody { + fn typeck_body<'tcx>( + _: &mut LateContext<'tcx>, + _: BodyId, + ) -> Option<&'tcx TypeckResults<'tcx>> { + None + } +} + +impl<'tcx, T: LateLintPass<'tcx>, Typeck: TypeckBody> LateContextAndPass<'tcx, T, Typeck> { /// Merge the lints specified by any lint attributes into the /// current lint context, call the provided function, then reset the /// lints in effect to their previous state. @@ -77,7 +103,9 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { } } -impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPass<'tcx, T> { +impl<'tcx, T: LateLintPass<'tcx>, Typeck: TypeckBody> hir_visit::Visitor<'tcx> + for LateContextAndPass<'tcx, T, Typeck> +{ type NestedFilter = nested_filter::All; /// Because lints are scoped lexically, we want to walk nested @@ -89,23 +117,18 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas fn visit_nested_body(&mut self, body_id: hir::BodyId) { let old_enclosing_body = self.context.enclosing_body.replace(body_id); - let old_cached_typeck_results = self.context.cached_typeck_results.get(); + let old_typeck_results = self.context.typeck_results; - // HACK(eddyb) avoid trashing `cached_typeck_results` when we're - // nested in `visit_fn`, which may have already resulted in them - // being queried. + // The body and typeck results are also set in `visit_fn`. + // Only fetch the results if this is for a new body. if old_enclosing_body != Some(body_id) { - self.context.cached_typeck_results.set(None); + Typeck::typeck_body(&mut self.context, body_id); } let body = self.context.tcx.hir_body(body_id); self.visit_body(body); self.context.enclosing_body = old_enclosing_body; - - // See HACK comment above. - if old_enclosing_body != Some(body_id) { - self.context.cached_typeck_results.set(old_cached_typeck_results); - } + self.context.typeck_results = old_typeck_results; } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { @@ -123,7 +146,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) { let generics = self.context.generics.take(); self.context.generics = it.kind.generics(); - let old_cached_typeck_results = self.context.cached_typeck_results.take(); + let old_typeck_results = self.context.typeck_results.take(); let old_enclosing_body = self.context.enclosing_body.take(); self.with_lint_attrs(it.hir_id(), |cx| { cx.with_param_env(it.owner_id, |cx| { @@ -133,7 +156,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas }); }); self.context.enclosing_body = old_enclosing_body; - self.context.cached_typeck_results.set(old_cached_typeck_results); + self.context.typeck_results = old_typeck_results; self.context.generics = generics; } @@ -189,12 +212,12 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas // Wrap in typeck results here, not just in visit_nested_body, // in order for `check_fn` to be able to use them. let old_enclosing_body = self.context.enclosing_body.replace(body_id); - let old_cached_typeck_results = self.context.cached_typeck_results.take(); + let old_typeck_results = Typeck::typeck_body(&mut self.context, body_id); let body = self.context.tcx.hir_body(body_id); lint_callback!(self, check_fn, fk, decl, body, span, id); hir_visit::walk_fn(self, fk, decl, body_id, id); self.context.enclosing_body = old_enclosing_body; - self.context.cached_typeck_results.set(old_cached_typeck_results); + self.context.typeck_results = old_typeck_results; } fn visit_variant_data(&mut self, s: &'tcx hir::VariantData<'tcx>) { @@ -333,6 +356,27 @@ macro_rules! impl_late_lint_pass { crate::late_lint_methods!(impl_late_lint_pass, []); +/// Runs only the `MissingDoc` lint pass without type checking bodies. +/// +/// **DO NOT** use this for anything other than rustdoc. This exists solely to workaround +/// the fact that rustdoc parses functions which would not pass type checking. See: +/// . +pub fn lint_missing_docs<'tcx>(tcx: TyCtxt<'tcx>, module_def_id: LocalModDefId) { + if is_lint_pass_required(tcx.skippable_lints(()), &MissingDoc.get_lints()) { + let context = LateContext { + tcx, + enclosing_body: None, + typeck_results: None, + param_env: ty::ParamEnv::empty(), + effective_visibilities: tcx.effective_visibilities(()), + last_node_with_lint_attrs: tcx.local_def_id_to_hir_id(module_def_id), + generics: None, + only_module: true, + }; + late_lint_mod_inner(tcx, module_def_id, context, MissingDoc, NoTypeckBody); + } +} + pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( tcx: TyCtxt<'tcx>, module_def_id: LocalModDefId, @@ -341,7 +385,7 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( let context = LateContext { tcx, enclosing_body: None, - cached_typeck_results: Cell::new(None), + typeck_results: None, param_env: ty::ParamEnv::empty(), effective_visibilities: tcx.effective_visibilities(()), last_node_with_lint_attrs: tcx.local_def_id_to_hir_id(module_def_id), @@ -363,14 +407,14 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( let builtin_lints_must_run = is_lint_pass_required(skippable_lints, &builtin_lints.get_lints()); if passes.is_empty() { if builtin_lints_must_run { - late_lint_mod_inner(tcx, module_def_id, context, builtin_lints); + late_lint_mod_inner(tcx, module_def_id, context, builtin_lints, ()); } } else { if builtin_lints_must_run { passes.push(Box::new(builtin_lints) as Box>); } let pass = RuntimeCombinedLateLintPass { passes }; - late_lint_mod_inner(tcx, module_def_id, context, pass); + late_lint_mod_inner(tcx, module_def_id, context, pass, ()); } } @@ -379,8 +423,9 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>( module_def_id: LocalModDefId, context: LateContext<'tcx>, pass: T, + typeck_body: impl TypeckBody, ) { - let mut cx = LateContextAndPass { context, pass }; + let mut cx = LateContextAndPass { context, pass, _typeck_body: typeck_body }; let (module, _span, hir_id) = tcx.hir_get_module(module_def_id); @@ -415,7 +460,7 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { let context = LateContext { tcx, enclosing_body: None, - cached_typeck_results: Cell::new(None), + typeck_results: None, param_env: ty::ParamEnv::empty(), effective_visibilities: tcx.effective_visibilities(()), last_node_with_lint_attrs: hir::CRATE_HIR_ID, @@ -424,7 +469,7 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { }; let pass = RuntimeCombinedLateLintPass { passes }; - let mut cx = LateContextAndPass { context, pass }; + let mut cx = LateContextAndPass { context, pass, _typeck_body: () }; // Visit the whole crate. cx.with_lint_attrs(hir::CRATE_HIR_ID, |cx| { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 14c70100c5f97..81fe09bd2baf7 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -133,11 +133,10 @@ use unused::must_use::*; use unused::*; #[rustfmt::skip] -pub use builtin::MissingDoc; pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore}; pub use early::diagnostics::DiagAndSess; pub use early::{EarlyCheckNode, check_ast_node}; -pub use late::{check_crate, late_lint_mod, unerased_lint_store}; +pub use late::{check_crate, late_lint_mod, lint_missing_docs, unerased_lint_store}; pub use levels::LintLevelsBuilder; pub use passes::{EarlyLintPass, LateLintPass}; pub use rustc_errors::BufferedEarlyLint; diff --git a/compiler/rustc_lint/src/unit_bindings.rs b/compiler/rustc_lint/src/unit_bindings.rs index ed015908ae54a..61c4a95c995b4 100644 --- a/compiler/rustc_lint/src/unit_bindings.rs +++ b/compiler/rustc_lint/src/unit_bindings.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitBindings { // - explicitly wrote `let pat = ();` // - explicitly wrote `let () = init;`. if !local.span.from_expansion() - && let Some(tyck_results) = cx.maybe_typeck_results() + && let Some(tyck_results) = cx.typeck_results && let Some(init) = local.init && let init_ty = tyck_results.expr_ty(init) && let local_ty = tyck_results.node_type(local.hir_id) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index fccfa3f57e930..e4a2f2d793073 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -14,7 +14,7 @@ use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{HirId, Path}; -use rustc_lint::{MissingDoc, late_lint_mod}; +use rustc_lint::lint_missing_docs; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; use rustc_session::config::{ @@ -310,8 +310,7 @@ pub(crate) fn create_config( override_queries: Some(|_sess, providers| { // We do not register late module lints, so this only runs `MissingDoc`. // Most lints will require typechecking, so just don't run them. - providers.queries.lint_mod = - |tcx, module_def_id| late_lint_mod(tcx, module_def_id, MissingDoc); + providers.queries.lint_mod = |tcx, module_def_id| lint_missing_docs(tcx, module_def_id); // hack so that `used_trait_imports` won't try to call typeck providers.queries.used_trait_imports = |_, _| { static EMPTY_SET: LazyLock> = LazyLock::new(UnordSet::default); diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index e49dee4164b88..0c19b1cf09095 100644 --- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -78,7 +78,7 @@ fn check_raw_ptr<'tcx>( fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option { if let (&hir::PatKind::Binding(_, id, _, _), Some(&ty::RawPtr(_, _))) = ( &arg.pat.kind, - cx.maybe_typeck_results() + cx.typeck_results .map(|typeck_results| typeck_results.pat_ty(arg.pat).kind()), ) { Some(id) diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 70176c62772b7..392adeea7abca 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -303,7 +303,7 @@ impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { fn new(cx: &'a LateContext<'tcx>, target: &'b ImplicitHasherType<'tcx>) -> Self { Self { cx, - maybe_typeck_results: cx.maybe_typeck_results(), + maybe_typeck_results: cx.typeck_results, target, suggestions: BTreeMap::new(), } diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs index 039da4cc4757a..d8bff9e9f1703 100644 --- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs +++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs @@ -174,7 +174,7 @@ impl PassByRefOrValue { && size <= self.ref_min_size && let hir::TyKind::Ref(_, MutTy { ty: decl_ty, .. }) = input.kind { - if let Some(typeck) = cx.maybe_typeck_results() + if let Some(typeck) = cx.typeck_results // Don't lint if a raw pointer is created. // TODO: Limit the check only to raw pointers to the argument (or part of the argument) // which escape the current function. diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 38724a1053f65..769523a5af11c 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -69,7 +69,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, - maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)), + maybe_typeck_results: cx.typeck_results.map(|x| (x, x)), allow_side_effects: true, expr_fallback: None, path_check: PathCheck::default(), @@ -1140,7 +1140,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, - maybe_typeck_results: cx.maybe_typeck_results(), + maybe_typeck_results: cx.typeck_results, s: FxHasher::default(), path_check: PathCheck::default(), } diff --git a/src/tools/clippy/clippy_utils/src/res.rs b/src/tools/clippy/clippy_utils/src/res.rs index 23afb25f1bbfe..59571a929887a 100644 --- a/src/tools/clippy/clippy_utils/src/res.rs +++ b/src/tools/clippy/clippy_utils/src/res.rs @@ -72,7 +72,7 @@ impl<'tcx> MaybeTypeckRes<'tcx> for LateContext<'tcx> { #[inline] #[cfg_attr(debug_assertions, track_caller)] fn typeck_res(&self) -> Option<&TypeckResults<'tcx>> { - if let Some(typeck) = self.maybe_typeck_results() { + if let Some(typeck) = self.typeck_results { Some(typeck) } else { // It's possible to get the `TypeckResults` for any other body, but diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 8cded245fac22..589eaa43ec569 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -41,7 +41,7 @@ pub use type_certainty::expr_type_is_certain; /// Lower a [`hir::Ty`] to a [`rustc_middle::ty::Ty`]. pub fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { - cx.maybe_typeck_results() + cx.typeck_results .filter(|results| results.hir_owner == hir_ty.hir_id.owner) .and_then(|results| results.node_type_opt(hir_ty.hir_id)) .unwrap_or_else(|| lower_ty(cx.tcx, hir_ty))