Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
25 changes: 5 additions & 20 deletions compiler/rustc_lint/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
//! See <https://rustc-dev-guide.rust-lang.org/diagnostics.html> for an
//! overview of how lints are implemented.

use std::cell::Cell;
use std::slice;

use rustc_abi as abi;
Expand Down Expand Up @@ -468,11 +467,8 @@ pub struct LateContext<'tcx> {
/// Current body, or `None` if outside a body.
pub enclosing_body: Option<hir::BodyId>,

/// 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<Option<&'tcx ty::TypeckResults<'tcx>>>,
/// 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>,
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
97 changes: 71 additions & 26 deletions compiler/rustc_lint/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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>) {
Expand All @@ -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| {
Expand All @@ -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;
}

Expand Down Expand Up @@ -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>) {
Expand Down Expand Up @@ -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:
/// <https://github.com/rust-lang/rust/pull/73566>.
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,
Expand All @@ -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),
Expand All @@ -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<dyn LateLintPass<'tcx>>);
}
let pass = RuntimeCombinedLateLintPass { passes };
late_lint_mod_inner(tcx, module_def_id, context, pass);
late_lint_mod_inner(tcx, module_def_id, context, pass, ());
}
}

Expand All @@ -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);

Expand Down Expand Up @@ -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,
Expand All @@ -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| {
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lint/src/unit_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 2 additions & 3 deletions src/librustdoc/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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<UnordSet<LocalDefId>> = LazyLock::new(UnordSet::default);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ fn check_raw_ptr<'tcx>(
fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option<HirId> {
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)
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/implicit_hasher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions src/tools/clippy/clippy_utils/src/hir_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
}
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_utils/src/res.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_utils/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Loading