Skip to content

Commit 67626b8

Browse files
committed
Auto merge of #113734 - cjgillot:no-crate-lint, r=petrochenkov
Convert builtin "global" late lints to run per module The compiler currently has 4 non-incremental lints: 1. `clashing_extern_declarations`; 2. `missing_debug_implementations`; 3. ~`unnameable_test_items`;~ changed by #114414 4. `missing_docs`. Non-incremental lints get reexecuted for each compilation, which is slow. Moreover, those lints are allow-by-default, so run for nothing most of the time. This PR attempts to make them more incremental-friendly. `clashing_extern_declarations` is moved to a standalone query. `missing_debug_implementation` can use `non_blanket_impls_for_ty` instead of recomputing it. `missing_docs` is harder as it needs to track if there is a `doc(hidden)` module surrounding. I hack around this using the lint level engine. That's easy to implement and allows to re-enable the lint for a re-exported module, while a more proper solution would reuse the same device as `unnameable_test_items`.
2 parents fca59ab + 905b63d commit 67626b8

16 files changed

+653
-671
lines changed

compiler/rustc_interface/src/passes.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -846,10 +846,11 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
846846
},
847847
{
848848
sess.time("lint_checking", || {
849-
rustc_lint::check_crate(tcx, || {
850-
rustc_lint::BuiltinCombinedLateLintPass::new()
851-
});
849+
rustc_lint::check_crate(tcx);
852850
});
851+
},
852+
{
853+
tcx.ensure().clashing_extern_declarations(());
853854
}
854855
);
855856
},

compiler/rustc_lint/src/builtin.rs

+23-440
Large diffs are not rendered by default.

compiler/rustc_lint/src/foreign_modules.rs

+402
Large diffs are not rendered by default.

compiler/rustc_lint/src/late.rs

+22-20
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore};
1818
use rustc_ast as ast;
1919
use rustc_data_structures::stack::ensure_sufficient_stack;
20-
use rustc_data_structures::sync::{join, DynSend};
20+
use rustc_data_structures::sync::join;
2121
use rustc_hir as hir;
2222
use rustc_hir::def_id::LocalDefId;
2323
use rustc_hir::intravisit as hir_visit;
@@ -336,7 +336,7 @@ macro_rules! impl_late_lint_pass {
336336

337337
crate::late_lint_methods!(impl_late_lint_pass, []);
338338

339-
pub(super) fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
339+
pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
340340
tcx: TyCtxt<'tcx>,
341341
module_def_id: LocalDefId,
342342
builtin_lints: T,
@@ -376,17 +376,32 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>(
376376
let mut cx = LateContextAndPass { context, pass };
377377

378378
let (module, _span, hir_id) = tcx.hir().get_module(module_def_id);
379+
380+
// There is no module lint that will have the crate itself as an item, so check it here.
381+
if hir_id == hir::CRATE_HIR_ID {
382+
lint_callback!(cx, check_crate,);
383+
}
384+
379385
cx.process_mod(module, hir_id);
380386

381387
// Visit the crate attributes
382388
if hir_id == hir::CRATE_HIR_ID {
383389
for attr in tcx.hir().attrs(hir::CRATE_HIR_ID).iter() {
384390
cx.visit_attribute(attr)
385391
}
392+
lint_callback!(cx, check_crate_post,);
386393
}
387394
}
388395

389-
fn late_lint_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>(tcx: TyCtxt<'tcx>, builtin_lints: T) {
396+
fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {
397+
// Note: `passes` is often empty.
398+
let mut passes: Vec<_> =
399+
unerased_lint_store(tcx).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
400+
401+
if passes.is_empty() {
402+
return;
403+
}
404+
390405
let context = LateContext {
391406
tcx,
392407
enclosing_body: None,
@@ -399,18 +414,8 @@ fn late_lint_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>(tcx: TyCtxt<'tcx>, builti
399414
only_module: false,
400415
};
401416

402-
// Note: `passes` is often empty. In that case, it's faster to run
403-
// `builtin_lints` directly rather than bundling it up into the
404-
// `RuntimeCombinedLateLintPass`.
405-
let mut passes: Vec<_> =
406-
unerased_lint_store(tcx).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
407-
if passes.is_empty() {
408-
late_lint_crate_inner(tcx, context, builtin_lints);
409-
} else {
410-
passes.push(Box::new(builtin_lints));
411-
let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] };
412-
late_lint_crate_inner(tcx, context, pass);
413-
}
417+
let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] };
418+
late_lint_crate_inner(tcx, context, pass);
414419
}
415420

416421
fn late_lint_crate_inner<'tcx, T: LateLintPass<'tcx>>(
@@ -432,15 +437,12 @@ fn late_lint_crate_inner<'tcx, T: LateLintPass<'tcx>>(
432437
}
433438

434439
/// Performs lint checking on a crate.
435-
pub fn check_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>(
436-
tcx: TyCtxt<'tcx>,
437-
builtin_lints: impl FnOnce() -> T + Send + DynSend,
438-
) {
440+
pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>) {
439441
join(
440442
|| {
441443
tcx.sess.time("crate_lints", || {
442444
// Run whole crate non-incremental lints
443-
late_lint_crate(tcx, builtin_lints());
445+
late_lint_crate(tcx);
444446
});
445447
},
446448
|| {

compiler/rustc_lint/src/levels.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::{
2+
builtin::MISSING_DOCS,
23
context::{CheckLintNameResult, LintStore},
34
fluent_generated as fluent,
45
late::unerased_lint_store,
@@ -667,6 +668,16 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
667668
continue;
668669
}
669670

671+
// `#[doc(hidden)]` disables missing_docs check.
672+
if attr.has_name(sym::doc)
673+
&& attr
674+
.meta_item_list()
675+
.map_or(false, |l| ast::attr::list_contains_name(&l, sym::hidden))
676+
{
677+
self.insert(LintId::of(MISSING_DOCS), (Level::Allow, LintLevelSource::Default));
678+
continue;
679+
}
680+
670681
let level = match Level::from_attr(attr) {
671682
None => continue,
672683
// This is the only lint level with a `LintExpectationId` that can be created from an attribute

compiler/rustc_lint/src/lib.rs

+15-30
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ mod enum_intrinsics_non_enums;
5959
mod errors;
6060
mod expect;
6161
mod for_loops_over_fallibles;
62+
mod foreign_modules;
6263
pub mod hidden_unicode_codepoints;
6364
mod internal;
6465
mod invalid_from_utf8;
@@ -125,11 +126,11 @@ use types::*;
125126
use unused::*;
126127

127128
/// Useful for other parts of the compiler / Clippy.
128-
pub use builtin::SoftLints;
129+
pub use builtin::{MissingDoc, SoftLints};
129130
pub use context::{CheckLintNameResult, FindLintError, LintStore};
130131
pub use context::{EarlyContext, LateContext, LintContext};
131132
pub use early::{check_ast_node, EarlyCheckNode};
132-
pub use late::{check_crate, unerased_lint_store};
133+
pub use late::{check_crate, late_lint_mod, unerased_lint_store};
133134
pub use passes::{EarlyLintPass, LateLintPass};
134135
pub use rustc_session::lint::Level::{self, *};
135136
pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId};
@@ -140,11 +141,12 @@ fluent_messages! { "../messages.ftl" }
140141
pub fn provide(providers: &mut Providers) {
141142
levels::provide(providers);
142143
expect::provide(providers);
144+
foreign_modules::provide(providers);
143145
*providers = Providers { lint_mod, ..*providers };
144146
}
145147

146148
fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
147-
late::late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new());
149+
late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new());
148150
}
149151

150152
early_lint_methods!(
@@ -182,25 +184,6 @@ early_lint_methods!(
182184
]
183185
);
184186

185-
// FIXME: Make a separate lint type which does not require typeck tables.
186-
187-
late_lint_methods!(
188-
declare_combined_late_lint_pass,
189-
[
190-
pub BuiltinCombinedLateLintPass,
191-
[
192-
// Tracks attributes of parents
193-
MissingDoc: MissingDoc::new(),
194-
// Builds a global list of all impls of `Debug`.
195-
// FIXME: Turn the computation of types which implement Debug into a query
196-
// and change this to a module lint pass
197-
MissingDebugImplementations: MissingDebugImplementations::default(),
198-
// Keeps a global list of foreign declarations.
199-
ClashingExternDeclarations: ClashingExternDeclarations::new(),
200-
]
201-
]
202-
);
203-
204187
late_lint_methods!(
205188
declare_combined_late_lint_pass,
206189
[
@@ -253,6 +236,8 @@ late_lint_methods!(
253236
OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
254237
MultipleSupertraitUpcastable: MultipleSupertraitUpcastable,
255238
MapUnitFn: MapUnitFn,
239+
MissingDebugImplementations: MissingDebugImplementations,
240+
MissingDoc: MissingDoc,
256241
]
257242
]
258243
);
@@ -281,7 +266,7 @@ fn register_builtins(store: &mut LintStore) {
281266
store.register_lints(&BuiltinCombinedPreExpansionLintPass::get_lints());
282267
store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints());
283268
store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints());
284-
store.register_lints(&BuiltinCombinedLateLintPass::get_lints());
269+
store.register_lints(&foreign_modules::get_lints());
285270

286271
add_lint_group!(
287272
"nonstandard_style",
@@ -521,20 +506,20 @@ fn register_internals(store: &mut LintStore) {
521506
store.register_lints(&LintPassImpl::get_lints());
522507
store.register_early_pass(|| Box::new(LintPassImpl));
523508
store.register_lints(&DefaultHashTypes::get_lints());
524-
store.register_late_pass(|_| Box::new(DefaultHashTypes));
509+
store.register_late_mod_pass(|_| Box::new(DefaultHashTypes));
525510
store.register_lints(&QueryStability::get_lints());
526-
store.register_late_pass(|_| Box::new(QueryStability));
511+
store.register_late_mod_pass(|_| Box::new(QueryStability));
527512
store.register_lints(&ExistingDocKeyword::get_lints());
528-
store.register_late_pass(|_| Box::new(ExistingDocKeyword));
513+
store.register_late_mod_pass(|_| Box::new(ExistingDocKeyword));
529514
store.register_lints(&TyTyKind::get_lints());
530-
store.register_late_pass(|_| Box::new(TyTyKind));
515+
store.register_late_mod_pass(|_| Box::new(TyTyKind));
531516
store.register_lints(&Diagnostics::get_lints());
532517
store.register_early_pass(|| Box::new(Diagnostics));
533-
store.register_late_pass(|_| Box::new(Diagnostics));
518+
store.register_late_mod_pass(|_| Box::new(Diagnostics));
534519
store.register_lints(&BadOptAccess::get_lints());
535-
store.register_late_pass(|_| Box::new(BadOptAccess));
520+
store.register_late_mod_pass(|_| Box::new(BadOptAccess));
536521
store.register_lints(&PassByValue::get_lints());
537-
store.register_late_pass(|_| Box::new(PassByValue));
522+
store.register_late_mod_pass(|_| Box::new(PassByValue));
538523
// FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and
539524
// `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and
540525
// these lints will trigger all of the time - change this once migration to diagnostic structs

compiler/rustc_lint/src/types.rs

+21-22
Original file line numberDiff line numberDiff line change
@@ -815,8 +815,7 @@ pub fn transparent_newtype_field<'a, 'tcx>(
815815
}
816816

817817
/// Is type known to be non-null?
818-
fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool {
819-
let tcx = cx.tcx;
818+
fn ty_is_known_nonnull<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool {
820819
match ty.kind() {
821820
ty::FnPtr(_) => true,
822821
ty::Ref(..) => true,
@@ -835,24 +834,21 @@ fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKi
835834

836835
def.variants()
837836
.iter()
838-
.filter_map(|variant| transparent_newtype_field(cx.tcx, variant))
839-
.any(|field| ty_is_known_nonnull(cx, field.ty(tcx, args), mode))
837+
.filter_map(|variant| transparent_newtype_field(tcx, variant))
838+
.any(|field| ty_is_known_nonnull(tcx, field.ty(tcx, args), mode))
840839
}
841840
_ => false,
842841
}
843842
}
844843

845844
/// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type.
846845
/// If the type passed in was not scalar, returns None.
847-
fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
848-
let tcx = cx.tcx;
846+
fn get_nullable_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
849847
Some(match *ty.kind() {
850848
ty::Adt(field_def, field_args) => {
851849
let inner_field_ty = {
852-
let mut first_non_zst_ty = field_def
853-
.variants()
854-
.iter()
855-
.filter_map(|v| transparent_newtype_field(cx.tcx, v));
850+
let mut first_non_zst_ty =
851+
field_def.variants().iter().filter_map(|v| transparent_newtype_field(tcx, v));
856852
debug_assert_eq!(
857853
first_non_zst_ty.clone().count(),
858854
1,
@@ -863,7 +859,7 @@ fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
863859
.expect("No non-zst fields in transparent type.")
864860
.ty(tcx, field_args)
865861
};
866-
return get_nullable_type(cx, inner_field_ty);
862+
return get_nullable_type(tcx, inner_field_ty);
867863
}
868864
ty::Int(ty) => Ty::new_int(tcx, ty),
869865
ty::Uint(ty) => Ty::new_uint(tcx, ty),
@@ -895,43 +891,44 @@ fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
895891
/// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes.
896892
/// FIXME: This duplicates code in codegen.
897893
pub(crate) fn repr_nullable_ptr<'tcx>(
898-
cx: &LateContext<'tcx>,
894+
tcx: TyCtxt<'tcx>,
895+
param_env: ty::ParamEnv<'tcx>,
899896
ty: Ty<'tcx>,
900897
ckind: CItemKind,
901898
) -> Option<Ty<'tcx>> {
902-
debug!("is_repr_nullable_ptr(cx, ty = {:?})", ty);
899+
debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty);
903900
if let ty::Adt(ty_def, args) = ty.kind() {
904901
let field_ty = match &ty_def.variants().raw[..] {
905902
[var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) {
906-
([], [field]) | ([field], []) => field.ty(cx.tcx, args),
903+
([], [field]) | ([field], []) => field.ty(tcx, args),
907904
_ => return None,
908905
},
909906
_ => return None,
910907
};
911908

912-
if !ty_is_known_nonnull(cx, field_ty, ckind) {
909+
if !ty_is_known_nonnull(tcx, field_ty, ckind) {
913910
return None;
914911
}
915912

916913
// At this point, the field's type is known to be nonnull and the parent enum is Option-like.
917914
// If the computed size for the field and the enum are different, the nonnull optimization isn't
918915
// being applied (and we've got a problem somewhere).
919-
let compute_size_skeleton = |t| SizeSkeleton::compute(t, cx.tcx, cx.param_env).unwrap();
916+
let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, param_env).unwrap();
920917
if !compute_size_skeleton(ty).same_size(compute_size_skeleton(field_ty)) {
921918
bug!("improper_ctypes: Option nonnull optimization not applied?");
922919
}
923920

924921
// Return the nullable type this Option-like enum can be safely represented with.
925-
let field_ty_abi = &cx.layout_of(field_ty).unwrap().abi;
922+
let field_ty_abi = &tcx.layout_of(param_env.and(field_ty)).unwrap().abi;
926923
if let Abi::Scalar(field_ty_scalar) = field_ty_abi {
927-
match field_ty_scalar.valid_range(cx) {
924+
match field_ty_scalar.valid_range(&tcx) {
928925
WrappingRange { start: 0, end }
929-
if end == field_ty_scalar.size(&cx.tcx).unsigned_int_max() - 1 =>
926+
if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 =>
930927
{
931-
return Some(get_nullable_type(cx, field_ty).unwrap());
928+
return Some(get_nullable_type(tcx, field_ty).unwrap());
932929
}
933930
WrappingRange { start: 1, .. } => {
934-
return Some(get_nullable_type(cx, field_ty).unwrap());
931+
return Some(get_nullable_type(tcx, field_ty).unwrap());
935932
}
936933
WrappingRange { start, end } => {
937934
unreachable!("Unhandled start and end range: ({}, {})", start, end)
@@ -1116,7 +1113,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11161113
if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none()
11171114
{
11181115
// Special-case types like `Option<extern fn()>`.
1119-
if repr_nullable_ptr(self.cx, ty, self.mode).is_none() {
1116+
if repr_nullable_ptr(self.cx.tcx, self.cx.param_env, ty, self.mode)
1117+
.is_none()
1118+
{
11201119
return FfiUnsafe {
11211120
ty,
11221121
reason: fluent::lint_improper_ctypes_enum_repr_reason,

compiler/rustc_middle/src/query/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1596,6 +1596,11 @@ rustc_queries! {
15961596
separate_provide_extern
15971597
}
15981598

1599+
/// Lint against `extern fn` declarations having incompatible types.
1600+
query clashing_extern_declarations(_: ()) {
1601+
desc { "checking `extern fn` declarations are compatible" }
1602+
}
1603+
15991604
/// Identifies the entry-point (e.g., the `main` function) for a given
16001605
/// crate, returning `None` if there is no entry point (such as for library crates).
16011606
query entry_fn(_: ()) -> Option<(DefId, EntryFnType)> {

src/librustdoc/core.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
1010
use rustc_hir::intravisit::{self, Visitor};
1111
use rustc_hir::{HirId, Path};
1212
use rustc_interface::interface;
13+
use rustc_lint::{late_lint_mod, MissingDoc};
1314
use rustc_middle::hir::nested_filter;
1415
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
1516
use rustc_session::config::{self, CrateType, ErrorOutputType, ResolveDocLinks};
@@ -262,8 +263,9 @@ pub(crate) fn create_config(
262263
parse_sess_created: None,
263264
register_lints: Some(Box::new(crate::lint::register_lints)),
264265
override_queries: Some(|_sess, providers, _external_providers| {
266+
// We do not register late module lints, so this only runs `MissingDoc`.
265267
// Most lints will require typechecking, so just don't run them.
266-
providers.lint_mod = |_, _| {};
268+
providers.lint_mod = |tcx, module_def_id| late_lint_mod(tcx, module_def_id, MissingDoc);
267269
// hack so that `used_trait_imports` won't try to call typeck
268270
providers.used_trait_imports = |_, _| {
269271
static EMPTY_SET: LazyLock<UnordSet<LocalDefId>> = LazyLock::new(UnordSet::default);
@@ -317,9 +319,7 @@ pub(crate) fn run_global_ctxt(
317319
tcx.hir().for_each_module(|module| tcx.ensure().check_mod_item_types(module))
318320
});
319321
tcx.sess.abort_if_errors();
320-
tcx.sess.time("missing_docs", || {
321-
rustc_lint::check_crate(tcx, rustc_lint::builtin::MissingDoc::new);
322-
});
322+
tcx.sess.time("missing_docs", || rustc_lint::check_crate(tcx));
323323
tcx.sess.time("check_mod_attrs", || {
324324
tcx.hir().for_each_module(|module| tcx.ensure().check_mod_attrs(module))
325325
});

0 commit comments

Comments
 (0)