Auto merge of #156576 - GuillaumeGomez:rollup-3CJ0vjd, r=GuillaumeGomez

Rollup of 3 pull requests

Successful merges:

 - rust-lang/rust#146220 (feat(rustdoc): stabilize `--emit` flag)
 - rust-lang/rust#153785 (Hand-written Debug implementation for `TypeTest`)
 - rust-lang/rust#156564 (Lint level cleanups)
This commit is contained in:
bors
2026-05-14 15:03:57 +00:00
39 changed files with 396 additions and 315 deletions
@@ -1,4 +1,5 @@
use std::collections::VecDeque;
use std::fmt;
use std::rc::Rc;
use rustc_data_structures::frozen::Frozen;
@@ -182,7 +183,7 @@ pub(crate) enum Cause {
/// For more information about this translation, see
/// `InferCtxt::process_registered_region_obligations` and
/// `InferCtxt::type_must_outlive` in `rustc_infer::infer::InferCtxt`.
#[derive(Clone, Debug)]
#[derive(Clone)]
pub(crate) struct TypeTest<'tcx> {
/// The type `T` that must outlive the region.
pub generic_kind: GenericKind<'tcx>,
@@ -198,6 +199,47 @@ pub(crate) struct TypeTest<'tcx> {
pub verify_bound: VerifyBound<'tcx>,
}
impl fmt::Debug for TypeTest<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt_bound(
f: &mut fmt::Formatter<'_>,
generic_kind: GenericKind<'_>,
lower: RegionVid,
bound: &VerifyBound<'_>,
) -> fmt::Result {
let fmt_bounds =
|f: &mut fmt::Formatter<'_>, bounds: &[VerifyBound<'_>]| -> fmt::Result {
let mut it = bounds.iter().peekable();
while let Some(bound) = it.next() {
fmt_bound(f, generic_kind, lower, bound)?;
if it.peek().is_some() {
write!(f, ", ")?
}
}
Ok(())
};
match bound {
VerifyBound::IfEq(binder) => write!(f, "{:?} == {:?}", generic_kind, binder),
VerifyBound::OutlivedBy(region) => write!(f, "{region:?}: {lower:?}"),
VerifyBound::AnyBound(verify_bounds) => {
write!(f, "Any[")?;
fmt_bounds(f, verify_bounds)?;
write!(f, "]")
}
VerifyBound::AllBounds(verify_bounds) => {
write!(f, "All[")?;
fmt_bounds(f, verify_bounds)?;
write!(f, "]")
}
VerifyBound::IsEmpty => write!(f, "Empty({lower:?})"),
}
}
write!(f, "TypeTest from {:?}[", self.span)?;
fmt_bound(f, self.generic_kind, self.lower_bound, &self.verify_bound)?;
write!(f, "] ⊢ {:?}: {:?}", self.generic_kind, self.lower_bound)
}
}
/// When we have an unmet lifetime constraint, we try to propagate it outward (e.g. to a closure
/// environment). If we can't, it is an error.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+8 -3
View File
@@ -60,7 +60,7 @@
use super::rpath::{self, RPathConfig};
use super::{apple, rmeta_link, versioned_llvm_target};
use crate::base::needs_allocator_shim_for_linking;
use crate::{CodegenLintLevels, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors};
use crate::{CodegenLintLevelSpecs, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors};
pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) {
if let Err(e) = fs::remove_file(path) {
@@ -728,7 +728,12 @@ fn is_windows_gnu_clang(sess: &Session) -> bool {
&& sess.target.options.cfg_abi == CfgAbi::Llvm
}
fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8], stderr: &[u8]) {
fn report_linker_output(
sess: &Session,
levels: CodegenLintLevelSpecs,
stdout: &[u8],
stderr: &[u8],
) {
let mut escaped_stderr = escape_string(&stderr);
let mut escaped_stdout = escape_string(&stdout);
let mut linker_info = String::new();
@@ -1106,7 +1111,7 @@ fn link_natively(
}
info!("reporting linker output: flavor={flavor:?}");
report_linker_output(sess, crate_info.lint_levels, &prog.stdout, &prog.stderr);
report_linker_output(sess, crate_info.lint_level_specs, &prog.stdout, &prog.stderr);
}
Err(e) => {
let linker_not_found = e.kind() == io::ErrorKind::NotFound;
+4 -2
View File
@@ -49,7 +49,9 @@
use crate::mir::operand::OperandValue;
use crate::mir::place::PlaceRef;
use crate::traits::*;
use crate::{CachedModuleCodegen, CodegenLintLevels, CrateInfo, ModuleCodegen, errors, meth, mir};
use crate::{
CachedModuleCodegen, CodegenLintLevelSpecs, CrateInfo, ModuleCodegen, errors, meth, mir,
};
pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate {
match (op, signed) {
@@ -953,7 +955,7 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo {
dependency_formats: Arc::clone(tcx.dependency_formats(())),
windows_subsystem,
natvis_debugger_visualizers: Default::default(),
lint_levels: CodegenLintLevels::from_tcx(tcx),
lint_level_specs: CodegenLintLevelSpecs::from_tcx(tcx),
metadata_symbol: exported_symbols::metadata_symbol_name(tcx),
each_linked_rlib_file_for_lto: Default::default(),
exported_symbols_for_lto: Default::default(),
+8 -8
View File
@@ -26,7 +26,7 @@
use rustc_macros::{Decodable, Encodable};
use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::WorkProduct;
use rustc_middle::lint::LevelAndSource;
use rustc_middle::lint::LevelSpec;
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
use rustc_middle::middle::dependency_format::Dependencies;
use rustc_middle::middle::exported_symbols::SymbolExportKind;
@@ -223,7 +223,7 @@ pub struct CrateInfo {
pub dependency_formats: Arc<Dependencies>,
pub windows_subsystem: Option<WindowsSubsystemKind>,
pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>,
pub lint_levels: CodegenLintLevels,
pub lint_level_specs: CodegenLintLevelSpecs,
pub metadata_symbol: String,
pub each_linked_rlib_file_for_lto: Vec<PathBuf>,
pub exported_symbols_for_lto: Vec<String>,
@@ -341,16 +341,16 @@ pub fn deserialize_rlink(
/// solely from the `.rlink` file. `Lint`s are defined too early to be encodeable.
/// Instead, encode exactly the information we need.
#[derive(Copy, Clone, Debug, Encodable, Decodable)]
pub struct CodegenLintLevels {
linker_messages: LevelAndSource,
linker_info: LevelAndSource,
pub struct CodegenLintLevelSpecs {
linker_messages: LevelSpec,
linker_info: LevelSpec,
}
impl CodegenLintLevels {
impl CodegenLintLevelSpecs {
pub fn from_tcx(tcx: TyCtxt<'_>) -> Self {
Self {
linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID),
linker_info: tcx.lint_level_at_node(LINKER_INFO, CRATE_HIR_ID),
linker_messages: tcx.lint_level_spec_at_node(LINKER_MESSAGES, CRATE_HIR_ID),
linker_info: tcx.lint_level_spec_at_node(LINKER_INFO, CRATE_HIR_ID),
}
}
}
@@ -735,11 +735,11 @@ fn increment_const_eval_counter(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<
let hir_id = ecx.machine.best_lint_scope(*ecx.tcx);
let is_error = ecx
.tcx
.lint_level_at_node(
.lint_level_spec_at_node(
rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL,
hir_id,
)
.level
.level()
.is_error();
let span = ecx.cur_span();
ecx.tcx.emit_node_span_lint(
+2 -2
View File
@@ -714,7 +714,7 @@ fn print_crate_info(
let lint_store = crate::unerased_lint_store(sess);
let features = rustc_expand::config::features(sess, attrs, crate_name);
let registered_tools = rustc_resolve::registered_tools_ast(sess.dcx(), attrs, sess);
let lint_levels = rustc_lint::LintLevelsBuilder::crate_root(
let builder = rustc_lint::LintLevelsBuilder::crate_root(
sess,
&features,
true,
@@ -729,7 +729,7 @@ fn print_crate_info(
// lint is unstable and feature gate isn't active, don't print
continue;
}
let level = lint_levels.lint_level(lint).level;
let level = builder.lint_level_spec(lint).level();
println_info!("{}={}", lint.name_lower(), level.as_str());
}
}
+2 -5
View File
@@ -2415,11 +2415,8 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis(
return false;
}
let level = tcx
.lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id)
.level;
!matches!(level, lint::Level::Allow)
!tcx.lint_level_spec_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id)
.is_allow()
}
/// Return a two string tuple (s1, s2)
+3 -5
View File
@@ -30,7 +30,6 @@
use rustc_hir::intravisit::FnKind as HirFnKind;
use rustc_hir::{self as hir, Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr};
use rustc_middle::bug;
use rustc_middle::lint::LevelAndSource;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{
@@ -61,7 +60,8 @@
BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment,
BuiltinUnusedDocCommentSub, BuiltinWhileTrue, EqInternalMethodImplemented, InvalidAsmLabel,
};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
declare_lint! {
/// The `while_true` lint detects `while true { }`.
///
@@ -695,9 +695,7 @@ fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
}
// Avoid listing trait impls if the trait is allowed.
let LevelAndSource { level, .. } =
cx.tcx.lint_level_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id());
if level == Level::Allow {
if cx.tcx.lint_level_spec_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id()).is_allow() {
return;
}
+7 -7
View File
@@ -20,7 +20,7 @@
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
use rustc_hir::{Pat, PatKind};
use rustc_middle::bug;
use rustc_middle::lint::LevelAndSource;
use rustc_middle::lint::LevelSpec;
use rustc_middle::middle::privacy::EffectiveVisibilities;
use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths};
@@ -537,8 +537,8 @@ fn emit_span_lint<S: Into<MultiSpan>>(
self.opt_span_lint(lint, Some(span), decorator);
}
/// This returns the lint level for the given lint at the current location.
fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource;
/// This returns the lint level spec for the given lint at the current location.
fn get_lint_level_spec(&self, lint: &'static Lint) -> LevelSpec;
/// This function can be used to manually fulfill an expectation. This can
/// be used for lints which contain several spans, and should be suppressed,
@@ -604,8 +604,8 @@ fn opt_span_lint<S: Into<MultiSpan>>(
}
}
fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs)
fn get_lint_level_spec(&self, lint: &'static Lint) -> LevelSpec {
self.tcx.lint_level_spec_at_node(lint, self.last_node_with_lint_attrs)
}
}
@@ -624,8 +624,8 @@ fn opt_span_lint<S: Into<MultiSpan>>(
self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorator)
}
fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
self.builder.lint_level(lint)
fn get_lint_level_spec(&self, lint: &'static Lint) -> LevelSpec {
self.builder.lint_level_spec(lint)
}
}
+91 -92
View File
@@ -9,11 +9,10 @@
use rustc_hir::HirId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_index::IndexVec;
use rustc_middle::bug;
use rustc_middle::hir::nested_filter;
use rustc_middle::lint::{
LevelAndSource, LintExpectation, LintLevelSource, ShallowLintLevelMap, emit_lint_base,
reveal_actual_level,
LevelSpec, LintExpectation, LintLevelSource, ShallowLintLevelMap, emit_lint_base,
reveal_actual_level_spec,
};
use rustc_middle::query::Providers;
use rustc_middle::ty::{RegisteredTools, TyCtxt};
@@ -65,9 +64,9 @@ struct LintStackIndex {
/// to find the specifications for a given lint.
#[derive(Debug)]
struct LintSet {
// -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
// -A,-W,-D flags, a `Symbol` for the flag itself and `LevelSpec` for which
// flag.
specs: FxIndexMap<LintId, LevelAndSource>,
specs: FxIndexMap<LintId, LevelSpec>,
parent: LintStackIndex,
}
@@ -76,32 +75,34 @@ fn new() -> Self {
LintLevelSets { list: IndexVec::new() }
}
fn get_lint_level(
fn get_lint_level_spec(
&self,
lint: &'static Lint,
idx: LintStackIndex,
aux: Option<&FxIndexMap<LintId, LevelAndSource>>,
aux: Option<&FxIndexMap<LintId, LevelSpec>>,
sess: &Session,
) -> LevelAndSource {
reveal_actual_level(sess, LintId::of(lint), |id| self.raw_lint_id_level(id, idx, aux))
) -> LevelSpec {
reveal_actual_level_spec(sess, LintId::of(lint), |id| {
self.raw_lint_level_spec(id, idx, aux)
})
}
fn raw_lint_id_level(
fn raw_lint_level_spec(
&self,
id: LintId,
mut idx: LintStackIndex,
aux: Option<&FxIndexMap<LintId, LevelAndSource>>,
) -> Option<LevelAndSource> {
aux: Option<&FxIndexMap<LintId, LevelSpec>>,
) -> Option<LevelSpec> {
if let Some(specs) = aux
&& let Some(level) = specs.get(&id)
&& let Some(level_spec) = specs.get(&id)
{
return Some(*level);
return Some(*level_spec);
}
loop {
let LintSet { ref specs, parent } = self.list[idx];
if let Some(level) = specs.get(&id) {
return Some(*level);
if let Some(level_spec) = specs.get(&id) {
return Some(*level_spec);
}
if idx == COMMAND_LINE {
return None;
@@ -125,11 +126,11 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet<LintId> {
!has_future_breakage && !lint.eval_always
})
.filter(|lint| {
let lint_level =
root_map.lint_level_id_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID);
let level_spec =
root_map.lint_level_spec_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID);
// Only include lints that are allowed at crate root or by default.
matches!(lint_level.level, Level::Allow)
|| (matches!(lint_level.src, LintLevelSource::Default)
level_spec.is_allow()
|| (matches!(level_spec.src, LintLevelSource::Default)
&& lint.default_level(tcx.sess.edition()) == Level::Allow)
})
.map(|lint| LintId::of(*lint))
@@ -140,8 +141,8 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet<LintId> {
// All lints that appear with a non-allow level must be run.
for (_, specs) in map.specs.iter() {
for (lint, level_and_source) in specs.iter() {
if !matches!(level_and_source.level, Level::Allow) {
for (lint, level_spec) in specs.iter() {
if !level_spec.is_allow() {
dont_need_to_run.remove(lint);
}
}
@@ -212,23 +213,23 @@ pub struct TopDown {
}
pub trait LintLevelsProvider {
fn current_specs(&self) -> &FxIndexMap<LintId, LevelAndSource>;
fn insert(&mut self, id: LintId, lvl: LevelAndSource);
fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource;
fn current_specs(&self) -> &FxIndexMap<LintId, LevelSpec>;
fn insert(&mut self, id: LintId, level_spec: LevelSpec);
fn get_lint_level_spec(&self, lint: &'static Lint, sess: &Session) -> LevelSpec;
fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation);
}
impl LintLevelsProvider for TopDown {
fn current_specs(&self) -> &FxIndexMap<LintId, LevelAndSource> {
fn current_specs(&self) -> &FxIndexMap<LintId, LevelSpec> {
&self.sets.list[self.cur].specs
}
fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
self.sets.list[self.cur].specs.insert(id, lvl);
fn insert(&mut self, id: LintId, level_spec: LevelSpec) {
self.sets.list[self.cur].specs.insert(id, level_spec);
}
fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource {
self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), sess)
fn get_lint_level_spec(&self, lint: &'static Lint, sess: &Session) -> LevelSpec {
self.sets.get_lint_level_spec(lint, self.cur, Some(self.current_specs()), sess)
}
fn push_expectation(&mut self, _: LintExpectationId, _: LintExpectation) {}
@@ -239,19 +240,19 @@ struct LintLevelQueryMap<'tcx> {
cur: HirId,
specs: ShallowLintLevelMap,
/// Empty hash map to simplify code.
empty: FxIndexMap<LintId, LevelAndSource>,
empty: FxIndexMap<LintId, LevelSpec>,
attrs: &'tcx hir::AttributeMap<'tcx>,
}
impl LintLevelsProvider for LintLevelQueryMap<'_> {
fn current_specs(&self) -> &FxIndexMap<LintId, LevelAndSource> {
fn current_specs(&self) -> &FxIndexMap<LintId, LevelSpec> {
self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
}
fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, lvl);
fn insert(&mut self, id: LintId, level_spec: LevelSpec) {
self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, level_spec);
}
fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
fn get_lint_level_spec(&self, lint: &'static Lint, _: &Session) -> LevelSpec {
self.specs.lint_level_spec_at_node(self.tcx, LintId::of(lint), self.cur)
}
fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation) {
self.specs.expectations.push((id, expectation))
@@ -455,12 +456,12 @@ pub(crate) fn features(&self) -> &Features {
self.features
}
fn current_specs(&self) -> &FxIndexMap<LintId, LevelAndSource> {
fn current_specs(&self) -> &FxIndexMap<LintId, LevelSpec> {
self.provider.current_specs()
}
fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
self.provider.insert(id, lvl)
fn insert(&mut self, id: LintId, level_spec: LevelSpec) {
self.provider.insert(id, level_spec)
}
fn add_command_line(&mut self) {
@@ -519,26 +520,31 @@ fn add_command_line(&mut self) {
};
for &id in ids {
// ForceWarn and Forbid cannot be overridden
if let Some(LevelAndSource { level: Level::ForceWarn | Level::Forbid, .. }) =
self.current_specs().get(&id)
if let Some(level_spec) = self.current_specs().get(&id)
&& matches!(level_spec.level(), Level::ForceWarn | Level::Forbid)
{
continue;
}
if self.check_gated_lint(id, DUMMY_SP, true) {
let src = LintLevelSource::CommandLine(lint_flag_val, level);
self.insert(id, LevelAndSource { level, lint_id: None, src });
self.insert(id, LevelSpec::new(level, None, src));
}
}
}
}
/// Attempts to insert the `id` to `level_src` map entry. If unsuccessful
/// Attempts to insert the `id` to `LevelSpec` map entry. If unsuccessful
/// (e.g. if a forbid was already inserted on the same scope), then emits a
/// diagnostic with no change to `specs`.
fn insert_spec(&mut self, id: LintId, LevelAndSource { level, lint_id, src }: LevelAndSource) {
let LevelAndSource { level: old_level, src: old_src, .. } =
self.provider.get_lint_level(id.lint, self.sess);
fn insert_spec(&mut self, id: LintId, level_spec: LevelSpec) {
let level = level_spec.level();
let lint_id = level_spec.lint_id();
let src = level_spec.src;
let old_level_spec = self.provider.get_lint_level_spec(id.lint, self.sess);
let old_level = old_level_spec.level();
let old_src = old_level_spec.src;
// Setting to a non-forbid level is an error if the lint previously had
// a forbid level. Note that this is not necessarily true even with a
@@ -621,15 +627,14 @@ fn insert_spec(&mut self, id: LintId, LevelAndSource { level, lint_id, src }: Le
match (old_level, level) {
// If the new level is an expectation store it in `ForceWarn`
(Level::ForceWarn, Level::Expect) => {
self.insert(id, LevelAndSource { level: Level::ForceWarn, lint_id, src: old_src })
self.insert(id, LevelSpec::new(Level::ForceWarn, lint_id, old_src))
}
// Keep `ForceWarn` level but drop the expectation
(Level::ForceWarn, _) => self.insert(
id,
LevelAndSource { level: Level::ForceWarn, lint_id: None, src: old_src },
),
(Level::ForceWarn, _) => {
self.insert(id, LevelSpec::new(Level::ForceWarn, None, old_src))
}
// Set the lint level as normal
_ => self.insert(id, LevelAndSource { level, lint_id, src }),
_ => self.insert(id, LevelSpec::new(level, lint_id, src)),
};
}
@@ -644,11 +649,7 @@ fn add(
if attr.is_automatically_derived_attr() {
self.insert(
LintId::of(SINGLE_USE_LIFETIMES),
LevelAndSource {
level: Level::Allow,
lint_id: None,
src: LintLevelSource::Default,
},
LevelSpec::new(Level::Allow, None, LintLevelSource::Default),
);
continue;
}
@@ -657,34 +658,28 @@ fn add(
if attr.is_doc_hidden() {
self.insert(
LintId::of(MISSING_DOCS),
LevelAndSource {
level: Level::Allow,
lint_id: None,
src: LintLevelSource::Default,
},
LevelSpec::new(Level::Allow, None, LintLevelSource::Default),
);
continue;
}
let (level, lint_id) = match Level::from_attr(attr.name(), || attr.id()) {
let (level, lint_id) = match Level::from_opt_symbol(attr.name()) {
None => continue,
// This is the only lint level with a `LintExpectationId` that can be created from
// an attribute.
Some((Level::Expect, Some(unstable_id))) if let Some(hir_id) = source_hir_id => {
let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = unstable_id
else {
bug!("stable id Level::from_attr")
// `Expect` is the only lint level with a `LintExpectationId` that can be created
// from an attribute.
Some(Level::Expect) => {
let id = if let Some(hir_id) = source_hir_id {
LintExpectationId::Stable {
hir_id,
attr_index: attr_index.try_into().unwrap(),
lint_index: None,
}
} else {
LintExpectationId::Unstable { attr_id: attr.id(), lint_index: None }
};
let stable_id = LintExpectationId::Stable {
hir_id,
attr_index: attr_index.try_into().unwrap(),
lint_index: None,
};
(Level::Expect, Some(stable_id))
(Level::Expect, Some(id))
}
Some((lvl, id)) => (lvl, id),
Some(level) => (level, None),
};
let Some(mut metas) = attr.meta_item_list() else { continue };
@@ -876,7 +871,7 @@ fn add(
let src = LintLevelSource::Node { name, span: sp, reason };
for &id in ids {
if self.check_gated_lint(id, sp, false) {
self.insert_spec(id, LevelAndSource { level, lint_id, src });
self.insert_spec(id, LevelSpec::new(level, lint_id, src));
}
}
@@ -907,12 +902,13 @@ fn add(
}
if self.lint_added_lints && !is_crate_node {
for (id, &LevelAndSource { level, ref src, .. }) in self.current_specs().iter() {
for (id, level_spec) in self.current_specs().iter() {
if !id.lint.crate_level_only {
continue;
}
let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } = *src
let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } =
level_spec.src
else {
continue;
};
@@ -920,7 +916,10 @@ fn add(
self.emit_span_lint(
UNUSED_ATTRIBUTES,
lint_attr_span.into(),
IgnoredUnlessCrateSpecified { level: level.as_str(), name: lint_attr_name },
IgnoredUnlessCrateSpecified {
level: level_spec.level().as_str(),
name: lint_attr_name,
},
);
// don't set a separate error for every lint in the group
break;
@@ -975,11 +974,11 @@ fn into_diag(
if self.lint_added_lints {
let lint = builtin::UNKNOWN_LINTS;
let level = self.lint_level(builtin::UNKNOWN_LINTS);
let level_spec = self.lint_level_spec(builtin::UNKNOWN_LINTS);
emit_lint_base(
self.sess,
lint,
level,
level_spec,
Some(span.into()),
UnknownLint { sess: &self.sess, lint_id, feature, lint_from_cli },
);
@@ -989,8 +988,8 @@ fn into_diag(
}
/// Find the lint level for a lint.
pub fn lint_level(&self, lint: &'static Lint) -> LevelAndSource {
self.provider.get_lint_level(lint, self.sess)
pub fn lint_level_spec(&self, lint: &'static Lint) -> LevelSpec {
self.provider.get_lint_level_spec(lint, self.sess)
}
/// Used to emit a lint-related diagnostic based on the current state of
@@ -1002,8 +1001,8 @@ pub(crate) fn opt_span_lint(
span: Option<MultiSpan>,
decorator: impl for<'a> Diagnostic<'a, ()>,
) {
let level = self.lint_level(lint);
emit_lint_base(self.sess, lint, level, span, decorator)
let level_spec = self.lint_level_spec(lint);
emit_lint_base(self.sess, lint, level_spec, span, decorator)
}
#[track_caller]
@@ -1013,14 +1012,14 @@ pub fn emit_span_lint(
span: MultiSpan,
decorator: impl for<'a> Diagnostic<'a, ()>,
) {
let level = self.lint_level(lint);
emit_lint_base(self.sess, lint, level, Some(span), decorator);
let level_spec = self.lint_level_spec(lint);
emit_lint_base(self.sess, lint, level_spec, Some(span), decorator);
}
#[track_caller]
pub fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> Diagnostic<'a, ()>) {
let level = self.lint_level(lint);
emit_lint_base(self.sess, lint, level, None, decorator);
let level_spec = self.lint_level_spec(lint);
emit_lint_base(self.sess, lint, level_spec, None, decorator);
}
}
+4 -7
View File
@@ -155,17 +155,14 @@ impl EarlyLintPass for NonAsciiIdents {
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
use std::collections::BTreeMap;
use rustc_session::lint::Level;
use rustc_span::Span;
use unicode_security::GeneralSecurityProfile;
let check_non_ascii_idents = cx.builder.lint_level(NON_ASCII_IDENTS).level != Level::Allow;
let check_uncommon_codepoints =
cx.builder.lint_level(UNCOMMON_CODEPOINTS).level != Level::Allow;
let check_confusable_idents =
cx.builder.lint_level(CONFUSABLE_IDENTS).level != Level::Allow;
let check_non_ascii_idents = !cx.builder.lint_level_spec(NON_ASCII_IDENTS).is_allow();
let check_uncommon_codepoints = !cx.builder.lint_level_spec(UNCOMMON_CODEPOINTS).is_allow();
let check_confusable_idents = !cx.builder.lint_level_spec(CONFUSABLE_IDENTS).is_allow();
let check_mixed_script_confusables =
cx.builder.lint_level(MIXED_SCRIPT_CONFUSABLES).level != Level::Allow;
!cx.builder.lint_level_spec(MIXED_SCRIPT_CONFUSABLES).is_allow();
if !check_non_ascii_idents
&& !check_uncommon_codepoints
+9 -31
View File
@@ -118,13 +118,6 @@ pub fn is_stable(&self) -> bool {
}
}
pub fn get_lint_index(&self) -> Option<u16> {
let (LintExpectationId::Unstable { lint_index, .. }
| LintExpectationId::Stable { lint_index, .. }) = self;
*lint_index
}
pub fn set_lint_index(&mut self, new_lint_index: Option<u16>) {
let (LintExpectationId::Unstable { lint_index, .. }
| LintExpectationId::Stable { lint_index, .. }) = self;
@@ -215,34 +208,19 @@ pub fn from_str(x: &str) -> Option<Self> {
}
}
/// Converts an `Attribute` to a level.
pub fn from_attr(
attr_name: Option<Symbol>,
attr_id: impl Fn() -> AttrId,
) -> Option<(Self, Option<LintExpectationId>)> {
attr_name.and_then(|name| Self::from_symbol(name, || Some(attr_id())))
/// Converts an `Option<Symbol>` to a level.
pub fn from_opt_symbol(s: Option<Symbol>) -> Option<Self> {
s.and_then(Self::from_symbol)
}
/// Converts a `Symbol` to a level.
pub fn from_symbol(
s: Symbol,
id: impl FnOnce() -> Option<AttrId>,
) -> Option<(Self, Option<LintExpectationId>)> {
pub fn from_symbol(s: Symbol) -> Option<Self> {
match s {
sym::allow => Some((Level::Allow, None)),
sym::expect => {
if let Some(attr_id) = id() {
Some((
Level::Expect,
Some(LintExpectationId::Unstable { attr_id, lint_index: None }),
))
} else {
None
}
}
sym::warn => Some((Level::Warn, None)),
sym::deny => Some((Level::Deny, None)),
sym::forbid => Some((Level::Forbid, None)),
sym::allow => Some(Level::Allow),
sym::expect => Some(Level::Expect),
sym::warn => Some(Level::Warn),
sym::deny => Some(Level::Deny),
sym::forbid => Some(Level::Forbid),
_ => None,
}
}
+5 -2
View File
@@ -328,8 +328,11 @@ pub fn report_unused_deps(&self, tcx: TyCtxt<'_>) {
return;
}
let level = tcx
.lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID)
.level;
.lint_level_spec_at_node(
lint::builtin::UNUSED_CRATE_DEPENDENCIES,
rustc_hir::CRATE_HIR_ID,
)
.level();
if level != lint::Level::Allow {
let unused_externs =
self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>();
+89 -49
View File
@@ -1,4 +1,4 @@
use std::cmp;
use std::cmp::min;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::sorted_map::SortedMap;
@@ -53,14 +53,59 @@ pub fn span(&self) -> Span {
}
}
/// Convenience helper for moving things around together that frequently are paired
/// Convenience helper for things that are frequently used together.
#[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable)]
pub struct LevelAndSource {
pub level: Level,
pub lint_id: Option<LintExpectationId>,
pub struct LevelSpec {
// This field *must* be private. It must be set in tandem with `lint_id`, only in
// `LevelSpec::new`, because only certain `level`/`lint_id` combinations are valid. See
// `LevelSpec::new` for those combinations.
//
// If you are thinking right now that `level` and `lint_id` should be combined into a single
// type that excludes the invalid combinations, that's a reasonable thought, but in practice
// it's painful because `level` needs to be used by itself, without `lint_id`, in many places.
// Making the fields private prevents invalid combinations while retaining the flexibility of
// two separate fields.
level: Level,
// This field *must* be private. See the comment on `level`.
lint_id: Option<LintExpectationId>,
pub src: LintLevelSource,
}
impl LevelSpec {
// Panics if an invalid `level`/`lint_id` combination is given.
pub fn new(
level: Level,
lint_id: Option<LintExpectationId>,
src: LintLevelSource,
) -> LevelSpec {
match (level, lint_id) {
(Level::Allow | Level::Warn | Level::Deny | Level::Forbid, None) => {}
(Level::Expect, Some(_)) => {}
(Level::ForceWarn, _) => {}
_ => panic!("invalid level/lint_id combination"),
}
LevelSpec { level, lint_id, src }
}
pub fn level(self) -> Level {
self.level
}
pub fn is_allow(self) -> bool {
self.level == Level::Allow
}
pub fn is_expect(self) -> bool {
self.level == Level::Expect
}
pub fn lint_id(self) -> Option<LintExpectationId> {
self.lint_id
}
}
/// Return type for the `shallow_lint_levels_on` query.
///
/// This map represents the set of allowed lints and allowance levels given
@@ -68,32 +113,32 @@ pub struct LevelAndSource {
#[derive(Default, Debug, StableHash)]
pub struct ShallowLintLevelMap {
pub expectations: Vec<(LintExpectationId, LintExpectation)>,
pub specs: SortedMap<ItemLocalId, FxIndexMap<LintId, LevelAndSource>>,
pub specs: SortedMap<ItemLocalId, FxIndexMap<LintId, LevelSpec>>,
}
/// Verify the effect of special annotations: `warnings` lint level and lint caps.
///
/// The return of this function is suitable for diagnostics.
pub fn reveal_actual_level(
pub fn reveal_actual_level_spec(
sess: &Session,
lint: LintId,
probe_for_lint_level: impl Fn(LintId) -> Option<LevelAndSource>,
) -> LevelAndSource {
let level = probe_for_lint_level(lint);
probe_for_lint_level_spec: impl Fn(LintId) -> Option<LevelSpec>,
) -> LevelSpec {
let level_spec = probe_for_lint_level_spec(lint);
// If `level` is none then we actually assume the default level for this lint.
let mut level = level.unwrap_or_else(|| LevelAndSource {
level: lint.lint.default_level(sess.edition()),
lint_id: None,
src: LintLevelSource::Default,
let mut level_spec = level_spec.unwrap_or_else(|| {
LevelSpec::new(lint.lint.default_level(sess.edition()), None, LintLevelSource::Default)
});
// If we're about to issue a warning, check at the last minute for any
// directives against the `warnings` lint group. If, for example, there's an
// `allow(warnings)` in scope then we want to respect that instead.
if level.level == Level::Warn {
if let Some(configured_level) = probe_for_lint_level(LintId::of(builtin::WARNINGS)) {
let respect_warnings_lint_group = match configured_level.level {
if level_spec.level == Level::Warn {
if let Some(configured_level_spec) =
probe_for_lint_level_spec(LintId::of(builtin::WARNINGS))
{
let respect_warnings_lint_group = match configured_level_spec.level {
// -Wwarnings is a no-op.
Level::Warn => false,
// Some warnings cannot be denied from the `warnings` lint group, only individually.
@@ -105,48 +150,46 @@ pub fn reveal_actual_level(
Level::Expect => true,
Level::ForceWarn => {
sess.dcx().span_delayed_bug(
configured_level.src.span(),
configured_level_spec.src.span(),
"cannot --force-warn the `warnings` lint group",
);
false
}
};
if respect_warnings_lint_group {
level = configured_level;
level_spec = configured_level_spec;
}
}
}
// Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn
level.level = if let LintLevelSource::CommandLine(_, Level::ForceWarn) = level.src {
level.level
} else {
cmp::min(level.level, sess.opts.lint_cap.unwrap_or(Level::Forbid))
if !matches!(level_spec.src, LintLevelSource::CommandLine(_, Level::ForceWarn)) {
level_spec.level = min(level_spec.level, sess.opts.lint_cap.unwrap_or(Level::Forbid));
};
// Ensure that we never exceed driver level.
if let Some(driver_level) = sess.driver_lint_caps.get(&lint) {
// Ensure that we never exceed driver level.
level.level = cmp::min(level.level, *driver_level);
level_spec.level = min(level_spec.level, *driver_level);
}
level
level_spec
}
impl ShallowLintLevelMap {
/// Perform a deep probe in the HIR tree looking for the actual level for the lint.
/// This lint level is not usable for diagnostics, it needs to be corrected by
/// Perform a deep probe in the HIR tree looking for the actual level spec for the lint.
/// This lint level spec is not usable for diagnostics, it needs to be corrected by
/// `reveal_actual_level` beforehand.
#[instrument(level = "trace", skip(self, tcx), ret)]
fn probe_for_lint_level(
fn probe_for_lint_level_spec(
&self,
tcx: TyCtxt<'_>,
id: LintId,
start: HirId,
) -> Option<LevelAndSource> {
) -> Option<LevelSpec> {
if let Some(map) = self.specs.get(&start.local_id)
&& let Some(level) = map.get(&id)
&& let Some(level_spec) = map.get(&id)
{
return Some(*level);
return Some(*level_spec);
}
let mut owner = start.owner;
@@ -158,31 +201,28 @@ fn probe_for_lint_level(
specs = &tcx.shallow_lint_levels_on(owner).specs;
}
if let Some(map) = specs.get(&parent.local_id)
&& let Some(level) = map.get(&id)
&& let Some(level_spec) = map.get(&id)
{
return Some(*level);
return Some(*level_spec);
}
}
None
}
/// Fetch and return the user-visible lint level for the given lint at the given HirId.
/// Fetch and return the user-visible lint level spec for the given lint at the given HirId.
#[instrument(level = "trace", skip(self, tcx), ret)]
pub fn lint_level_id_at_node(
&self,
tcx: TyCtxt<'_>,
lint: LintId,
cur: HirId,
) -> LevelAndSource {
reveal_actual_level(tcx.sess, lint, |lint| self.probe_for_lint_level(tcx, lint, cur))
pub fn lint_level_spec_at_node(&self, tcx: TyCtxt<'_>, lint: LintId, cur: HirId) -> LevelSpec {
reveal_actual_level_spec(tcx.sess, lint, |lint| {
self.probe_for_lint_level_spec(tcx, lint, cur)
})
}
}
impl TyCtxt<'_> {
/// Fetch and return the user-visible lint level for the given lint at the given HirId.
pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> LevelAndSource {
self.shallow_lint_levels_on(id.owner).lint_level_id_at_node(self, LintId::of(lint), id)
/// Fetch and return the user-visible lint level spec for the given lint at the given HirId.
pub fn lint_level_spec_at_node(self, lint: &'static Lint, id: HirId) -> LevelSpec {
self.shallow_lint_levels_on(id.owner).lint_level_spec_at_node(self, LintId::of(lint), id)
}
}
@@ -322,7 +362,7 @@ fn explain_lint_level_source(
pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>(
sess: &'a Session,
lint: &'static Lint,
level: LevelAndSource,
level_spec: LevelSpec,
span: Option<MultiSpan>,
decorate: D,
) {
@@ -332,13 +372,13 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>(
fn emit_lint_base_impl<'a>(
sess: &'a Session,
lint: &'static Lint,
level: LevelAndSource,
level_spec: LevelSpec,
span: Option<MultiSpan>,
decorate: Box<
dyn FnOnce(rustc_errors::DiagCtxtHandle<'a>, rustc_errors::Level) -> Diag<'a, ()> + 'a,
>,
) {
let LevelAndSource { level, lint_id, src } = level;
let LevelSpec { level, lint_id, src } = level_spec;
// Check for future incompatibility lints and issue a stronger warning.
let future_incompatible = lint.future_incompatible;
@@ -516,7 +556,7 @@ fn emit_lint_base_impl<'a>(
emit_lint_base_impl(
sess,
lint,
level,
level_spec,
span,
Box::new(move |dcx, level| decorate.into_diag(dcx, level)),
);
@@ -13,7 +13,7 @@
use rustc_session::Session;
use rustc_session::errors::feature_err_issue;
use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE};
use rustc_session::lint::{DeprecatedSinceKind, Level, Lint};
use rustc_session::lint::{DeprecatedSinceKind, Lint};
use rustc_span::{Span, Symbol, sym};
use tracing::debug;
@@ -234,7 +234,7 @@ fn late_report_deprecation(
// Calculating message for lint involves calling `self.def_path_str`,
// which will by default invoke the expensive `visible_parent_map` query.
// Skip all that work if the lint is allowed anyway.
if tcx.lint_level_at_node(lint, hir_id).level == Level::Allow {
if tcx.lint_level_spec_at_node(lint, hir_id).is_allow() {
return;
}
+4 -4
View File
@@ -2459,8 +2459,8 @@ pub fn emit_node_span_lint(
span: impl Into<MultiSpan>,
decorator: impl for<'a> Diagnostic<'a, ()>,
) {
let level = self.lint_level_at_node(lint, hir_id);
emit_lint_base(self.sess, lint, level, Some(span.into()), decorator)
let level_spec = self.lint_level_spec_at_node(lint, hir_id);
emit_lint_base(self.sess, lint, level_spec, Some(span.into()), decorator)
}
/// Find the appropriate span where `use` and outer attributes can be inserted at.
@@ -2502,8 +2502,8 @@ pub fn emit_node_lint(
id: HirId,
decorator: impl for<'a> Diagnostic<'a, ()>,
) {
let level = self.lint_level_at_node(lint, id);
emit_lint_base(self.sess, lint, level, None, decorator);
let level_spec = self.lint_level_spec_at_node(lint, id);
emit_lint_base(self.sess, lint, level_spec, None, decorator);
}
pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx [TraitCandidate<'tcx>]> {
@@ -1302,7 +1302,7 @@ fn maybe_lint_level_root_bounded(&mut self, orig_id: HirId) -> HirId {
.tcx
.hir_attrs(id)
.iter()
.any(|attr| Level::from_attr(attr.name(), || attr.id()).is_some())
.any(|attr| Level::from_opt_symbol(attr.name()).is_some())
{
// This is a rare case. It's for a node path that doesn't reach the root due to an
// intervening lint level attribute. This result doesn't get cached.
@@ -12,7 +12,6 @@
use rustc_middle::thir::*;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::lint::Level;
use rustc_session::lint::builtin::{DEPRECATED_SAFE_2024, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::{Span, Symbol};
@@ -175,7 +174,7 @@ fn warn_unused_unsafe(
/// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).level == Level::Allow
self.tcx.lint_level_spec_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).is_allow()
}
/// Handle closures/coroutines/inline-consts, which is unsafecked with their parent body.
@@ -233,10 +232,7 @@ fn visit_block(&mut self, block: &'a Block) {
});
}
BlockSafety::ExplicitUnsafe(hir_id) => {
let used = matches!(
self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id).level,
Level::Allow
);
let used = self.tcx.lint_level_spec_at_node(UNUSED_UNSAFE, hir_id).is_allow();
self.in_safety_context(
SafetyContext::UnsafeBlock {
span: block.span,
@@ -8,7 +8,6 @@
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, MatchSource};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint_defs::Level;
use rustc_middle::bug;
use rustc_middle::thir::visit::Visitor;
use rustc_middle::thir::*;
@@ -1026,7 +1025,7 @@ fn find_fallback_pattern_typo<'tcx>(
pat: &Pat<'tcx>,
lint: &mut UnreachablePattern<'_>,
) {
if let Level::Allow = cx.tcx.lint_level_at_node(UNREACHABLE_PATTERNS, hir_id).level {
if cx.tcx.lint_level_spec_at_node(UNREACHABLE_PATTERNS, hir_id).is_allow() {
// This is because we use `with_no_trimmed_paths` later, so if we never emit the lint we'd
// ICE. At the same time, we don't really need to do all of this if we won't emit anything.
return;
+18 -17
View File
@@ -779,7 +779,7 @@ fn has_allow_dead_code_or_lang_attr(
) -> Option<ComesFromAllowExpect> {
fn has_allow_expect_dead_code(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
let hir_id = tcx.local_def_id_to_hir_id(def_id);
let lint_level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).level;
let lint_level = tcx.lint_level_spec_at_node(lint::builtin::DEAD_CODE, hir_id).level();
matches!(lint_level, lint::Allow | lint::Expect)
}
@@ -1035,7 +1035,7 @@ fn mark_live_symbols_and_ignored_derived_traits(
struct DeadItem {
def_id: LocalDefId,
name: Symbol,
level: (lint::Level, Option<LintExpectationId>),
level_plus: (lint::Level, Option<LintExpectationId>),
}
struct DeadVisitor<'tcx> {
@@ -1082,10 +1082,10 @@ fn should_warn_about_field(&mut self, field: &ty::FieldDef) -> ShouldWarnAboutFi
ShouldWarnAboutField::Yes
}
fn def_lint_level(&self, id: LocalDefId) -> (lint::Level, Option<LintExpectationId>) {
fn def_lint_level_plus(&self, id: LocalDefId) -> (lint::Level, Option<LintExpectationId>) {
let hir_id = self.tcx.local_def_id_to_hir_id(id);
let level = self.tcx.lint_level_at_node(self.target_lint, hir_id);
(level.level, level.lint_id)
let level_spec = self.tcx.lint_level_spec_at_node(self.target_lint, hir_id);
(level_spec.level(), level_spec.lint_id())
}
fn dead_code_pub_in_binary_note(&self) -> Option<DeadCodePubInBinaryNote> {
@@ -1108,8 +1108,8 @@ fn lint_at_single_level(
let Some(&first_item) = dead_codes.first() else { return };
let tcx = self.tcx;
let first_lint_level = first_item.level;
assert!(dead_codes.iter().skip(1).all(|item| item.level == first_lint_level));
let first_lint_level_plus = first_item.level_plus;
assert!(dead_codes.iter().skip(1).all(|item| item.level_plus == first_lint_level_plus));
let names: Vec<_> = dead_codes.iter().map(|item| item.name).collect();
let spans: Vec<_> = dead_codes
@@ -1266,9 +1266,10 @@ fn warn_multiple(
if dead_codes.is_empty() {
return;
}
// FIXME: `dead_codes` should probably be morally equivalent to `IndexMap<(Level, LintExpectationId), (DefId, Symbol)>`
dead_codes.sort_by_key(|v| v.level.0);
for group in dead_codes.chunk_by(|a, b| a.level == b.level) {
// FIXME: `dead_codes` should probably be morally equivalent to
// `IndexMap<(Level, LintExpectationId), (DefId, Symbol)>`
dead_codes.sort_by_key(|v| v.level_plus.0);
for group in dead_codes.chunk_by(|a, b| a.level_plus == b.level_plus) {
self.lint_at_single_level(&group, participle, Some(def_id), report_on);
}
}
@@ -1277,7 +1278,7 @@ fn warn_dead_code(&mut self, id: LocalDefId, participle: &str) {
let item = DeadItem {
def_id: id,
name: self.tcx.item_name(id.to_def_id()),
level: self.def_lint_level(id),
level_plus: self.def_lint_level_plus(id),
};
self.lint_at_single_level(&[&item], participle, None, ReportOn::NamedField);
}
@@ -1381,8 +1382,8 @@ fn lint_dead_codes<'tcx>(
&& !visitor.is_live_code(local_def_id)
{
let name = tcx.item_name(def_id);
let level = visitor.def_lint_level(local_def_id);
dead_codes.push(DeadItem { def_id: local_def_id, name, level });
let level_plus = visitor.def_lint_level_plus(local_def_id);
dead_codes.push(DeadItem { def_id: local_def_id, name, level_plus });
}
}
}
@@ -1408,8 +1409,8 @@ fn lint_dead_codes<'tcx>(
let def_id = variant.def_id.expect_local();
if !live_symbols.contains(&def_id) {
// Record to group diagnostics.
let level = visitor.def_lint_level(def_id);
dead_variants.push(DeadItem { def_id, name: variant.name, level });
let level_plus = visitor.def_lint_level_plus(def_id);
dead_variants.push(DeadItem { def_id, name: variant.name, level_plus });
continue;
}
@@ -1424,8 +1425,8 @@ fn lint_dead_codes<'tcx>(
.filter_map(|field| {
let def_id = field.did.expect_local();
if let ShouldWarnAboutField::Yes = visitor.should_warn_about_field(field) {
let level = visitor.def_lint_level(def_id);
Some(DeadItem { def_id, name: field.name, level })
let level_plus = visitor.def_lint_level_plus(def_id);
Some(DeadItem { def_id, name: field.name, level_plus })
} else {
None
}
+1 -3
View File
@@ -843,9 +843,7 @@ fn visit_path(&mut self, path: &hir::Path<'tcx>, id: hir::HirId) {
// Calculating message for lint involves calling `self.def_path_str`,
// which will by default invoke the expensive `visible_parent_map` query.
// Skip all that work if the lint is allowed anyway.
if self.tcx.lint_level_at_node(DEPRECATED, id).level
== lint::Level::Allow
{
if self.tcx.lint_level_spec_at_node(DEPRECATED, id).is_allow() {
return;
}
// Show a deprecation message.
+10 -9
View File
@@ -1,4 +1,3 @@
use rustc_middle::lint::LevelAndSource;
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_span::ErrorGuaranteed;
use tracing::instrument;
@@ -64,10 +63,11 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
pat_column: &PatternColumn<'p, RustcPatCtxt<'p, 'tcx>>,
scrut_ty: RevealedTy<'tcx>,
) -> Result<(), ErrorGuaranteed> {
if !matches!(
rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).level,
rustc_session::lint::Level::Allow
) {
if !rcx
.tcx
.lint_level_spec_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level)
.is_allow()
{
let witnesses = collect_nonexhaustive_missing_variants(rcx, pat_column)?;
if !witnesses.is_empty() {
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
@@ -89,12 +89,13 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
// arm. This no longer makes sense so we warn users, to avoid silently breaking their
// usage of the lint.
for arm in arms {
let LevelAndSource { level, src, .. } =
rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data);
if !matches!(level, rustc_session::lint::Level::Allow) {
let level_spec =
rcx.tcx.lint_level_spec_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data);
let level = level_spec.level();
if level != rustc_session::lint::Level::Allow {
rcx.tcx.dcx().emit_warn(NonExhaustiveOmittedPatternLintOnArm {
span: arm.pat.data().span,
lint_span: src.span(),
lint_span: level_spec.src.span(),
suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()),
lint_level: level.as_str(),
lint_name: "non_exhaustive_omitted_patterns",
@@ -440,6 +440,37 @@ When `rustdoc` receives this flag, it will print an extra "Version (version)" in
the crate root's docs. You can use this flag to differentiate between different versions of your
library's documentation.
## `--emit`: control the types of output for rustdoc to emit
This flag controls the types of output by rustdoc. It accepts a comma-separated
list of values, and may be specified multiple times. The valid emit kinds are:
- `html-static-files` --- Generates shared static files whose contents are
tied to a specific toolchain version. These are written with a filename
that includes a hash of their contents, so they are safe to cache with
the change if the toolchain version or their contents change, or with the
`Cache-Control: immutable` directive.
- `html-non-static-files` --- Generates files based on the crate(s) being
documented. These file names need to be deterministic so there is no
content-hash in their file names.
- `dep-info` --- Generates a file with Makefile syntax that indicates all the
source files that were loaded to document the crate. The default output
filename is `CRATE_NAME.d`. This emit type can can optionally be followed by
`=` to specify an explicit output path, for example,
`--emit=dep-info=/path/to/foo.d`. The output can be sent to stdout by
specifying `-` as the path (e.g., `--emit=dep-info=-`).
Using this flag looks like this:
```bash
$ rustdoc src/lib.rs --emit=html-static-files,html-non-static-files,dep-info=/path/to/build/cache/foo.d
```
If not specified, the default emit types would be
`--emit=html-static-files,html-non-static-files`.
Output files are written to the current directory unless the `--out-dir` flag is used.
## `-`: load source code from the standard input
If you specify `-` as the INPUT on the command line, then `rustdoc` will read the
+1 -1
View File
@@ -534,7 +534,7 @@ fn opts() -> Vec<RustcOptGroup> {
"",
),
opt(
Unstable,
Stable,
Multi,
"",
"emit",
@@ -5,8 +5,7 @@
use rustc_hir as hir;
use rustc_lint::builtin::MISSING_DOCS;
use rustc_middle::lint::{LevelAndSource, LintLevelSource};
use rustc_session::lint;
use rustc_middle::lint::LintLevelSource;
use rustc_span::{FileName, RemapPathScopeComponents};
use serde::Serialize;
use tracing::debug;
@@ -222,8 +221,7 @@ fn visit_item(&mut self, i: &clean::Item) {
let has_doc_example = tests.found_tests != 0;
let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap();
let LevelAndSource { level, src, .. } =
self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id);
let level_spec = self.ctx.tcx.lint_level_spec_at_node(MISSING_DOCS, hir_id);
// In case we have:
//
@@ -258,7 +256,8 @@ fn visit_item(&mut self, i: &clean::Item) {
// unless the user had an explicit `allow`.
//
let should_have_docs = !should_be_ignored
&& (level != lint::Level::Allow || matches!(src, LintLevelSource::Default));
&& (!level_spec.is_allow()
|| matches!(level_spec.src, LintLevelSource::Default));
if let Some(span) = i.span(self.ctx.tcx) {
let filename = span.filename(self.ctx.sess());
@@ -7,8 +7,7 @@
use rustc_hir as hir;
use rustc_macros::Diagnostic;
use rustc_middle::lint::{LevelAndSource, LintLevelSource};
use rustc_session::lint;
use rustc_middle::lint::LintLevelSource;
use tracing::debug;
use super::Pass;
@@ -110,11 +109,11 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -
{
return false;
}
let LevelAndSource { level, src, .. } = cx.tcx.lint_level_at_node(
let level_spec = cx.tcx.lint_level_spec_at_node(
crate::lint::MISSING_DOC_CODE_EXAMPLES,
cx.tcx.local_def_id_to_hir_id(def_id),
);
level != lint::Level::Allow || matches!(src, LintLevelSource::Default)
!level_spec.is_allow() || matches!(level_spec.src, LintLevelSource::Default)
}
pub(crate) fn look_for_tests(cx: &DocContext<'_>, dox: &str, item: &Item) {
@@ -583,7 +583,7 @@ fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
if matches!(name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
allow_attributes_without_reason::check(cx, name, items, attr);
}
if is_lint_level(name, attr.id) {
if is_lint_level(name) {
blanket_clippy_restriction_lints::check(cx, name, items);
}
if items.is_empty() || !attr.has_name(sym::deprecated) {
@@ -15,7 +15,7 @@ pub(super) fn check(
) {
if cfg_attr.has_name(sym::clippy)
&& let Some(ident) = behind_cfg_attr.ident()
&& Level::from_symbol(ident.name, || Some(attr.id)).is_some()
&& Level::from_symbol(ident.name).is_some()
&& let Some(items) = behind_cfg_attr.meta_item_list()
{
let nb_items = items.len();
@@ -15,7 +15,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) {
return;
}
if let Some(lint_list) = &attr.meta_item_list()
&& attr.name().is_some_and(|name| is_lint_level(name, attr.id))
&& attr.name().is_some_and(|name| is_lint_level(name))
{
for lint in lint_list {
match item.kind {
@@ -1,5 +1,5 @@
use clippy_utils::macros::{is_panic, macro_backtrace};
use rustc_ast::{AttrId, MetaItemInner};
use rustc_ast::MetaItemInner;
use rustc_hir::{
Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
};
@@ -16,8 +16,8 @@ pub(super) fn is_word(nmi: &MetaItemInner, expected: Symbol) -> bool {
}
}
pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool {
Level::from_symbol(symbol, || Some(attr_id)).is_some()
pub(super) fn is_lint_level(symbol: Symbol) -> bool {
Level::from_symbol(symbol).is_some()
}
pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
@@ -11,7 +11,7 @@
use rustc_errors::Applicability;
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, RustcVersion, UnOp};
use rustc_lint::{LateContext, LateLintPass, Level};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
use rustc_span::def_id::LocalDefId;
use rustc_span::{Span, Symbol, SyntaxContext};
@@ -204,7 +204,7 @@ fn check_simplify_not(cx: &LateContext<'_>, msrv: Msrv, expr: &Expr<'_>) {
&& !expr.span.from_expansion()
&& !inner.span.from_expansion()
&& let Some(suggestion) = simplify_not(cx, msrv, inner)
&& cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).level != Level::Allow
&& !cx.tcx.lint_level_spec_at_node(NONMINIMAL_BOOL, expr.hir_id).is_allow()
{
use clippy_utils::sugg::{Sugg, has_enclosing_paren};
let maybe_par = if let Some(sug) = Sugg::hir_opt(cx, inner) {
@@ -612,7 +612,7 @@ fn bool_expr(&self, e: &'tcx Expr<'_>) {
}
}
let nonminimal_bool_lint = |mut suggestions: Vec<_>| {
if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).level != Level::Allow {
if !self.cx.tcx.lint_level_spec_at_node(NONMINIMAL_BOOL, e.hir_id).is_allow() {
suggestions.sort();
span_lint_hir_and_then(
self.cx,
@@ -238,7 +238,7 @@ fn check_significant_tokens_and_expect_attrs(
},
[attr]
if matches!(Level::from_attr(attr.name(), || attr.id()), Some((Level::Expect, _)))
if matches!(Level::from_opt_symbol(attr.name()), Some(Level::Expect))
&& let Some(metas) = attr.meta_item_list()
&& let Some(MetaItemInner::MetaItem(meta_item)) = metas.first()
&& let [tool, lint_name] = meta_item.path.segments.as_slice()
@@ -2,7 +2,7 @@
use clippy_utils::diagnostics::span_lint;
use rustc_ast::ast;
use rustc_data_structures::fx::FxHashSet;
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_session::impl_lint_pass;
use unicode_script::{Script, UnicodeScript};
@@ -69,7 +69,7 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
// Implementation is heavily inspired by the implementation of [`non_ascii_idents`] lint:
// https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_lint/src/non_ascii_idents.rs
let check_disallowed_script_idents = cx.builder.lint_level(DISALLOWED_SCRIPT_IDENTS).level != Level::Allow;
let check_disallowed_script_idents = !cx.builder.lint_level_spec(DISALLOWED_SCRIPT_IDENTS).is_allow();
if !check_disallowed_script_idents {
return;
}
@@ -2,7 +2,7 @@
use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind};
use rustc_errors::MultiSpan;
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
use rustc_middle::lint::LevelAndSource;
use rustc_middle::lint::LevelSpec;
use rustc_session::impl_lint_pass;
use rustc_span::{FileName, Span};
use std::collections::BTreeMap;
@@ -51,7 +51,7 @@
struct Modules {
local_path: PathBuf,
spans: Vec<Span>,
lint_levels: Vec<LevelAndSource>,
lint_level_specs: Vec<LevelSpec>,
}
#[derive(Default)]
@@ -71,10 +71,10 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
let modules = self.modules.entry(absolute_path).or_insert(Modules {
local_path,
spans: Vec::new(),
lint_levels: Vec::new(),
lint_level_specs: Vec::new(),
});
modules.spans.push(item.span_with_attributes());
modules.lint_levels.push(cx.get_lint_level(DUPLICATE_MOD));
modules.lint_level_specs.push(cx.get_lint_level_spec(DUPLICATE_MOD));
}
}
@@ -82,7 +82,7 @@ fn check_crate_post(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
for Modules {
local_path,
spans,
lint_levels,
lint_level_specs,
} in self.modules.values()
{
if spans.len() < 2 {
@@ -90,16 +90,16 @@ fn check_crate_post(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
}
// At this point the lint would be emitted
assert_eq!(spans.len(), lint_levels.len());
assert_eq!(spans.len(), lint_level_specs.len());
let spans: Vec<_> = spans
.iter()
.zip(lint_levels)
.filter_map(|(span, lvl)| {
if let Some(id) = lvl.lint_id {
.zip(lint_level_specs)
.filter_map(|(span, level_spec)| {
if let Some(id) = level_spec.lint_id() {
cx.fulfill_expectation(id);
}
(!matches!(lvl.level, Level::Allow | Level::Expect)).then_some(*span)
(!matches!(level_spec.level(), Level::Allow | Level::Expect)).then_some(*span)
})
.collect();
@@ -5,8 +5,7 @@
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt};
use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource, find_attr};
use rustc_lint::{LateContext, LateLintPass, Level, LintContext};
use rustc_middle::lint::LevelAndSource;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::impl_lint_pass;
use rustc_span::{Span, SyntaxContext};
use std::collections::BTreeMap;
@@ -252,17 +251,15 @@ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
.flatten()
.copied()
.inspect(|&unsafe_block| {
if let LevelAndSource {
level: Level::Expect,
lint_id: Some(id),
..
} = cx.tcx.lint_level_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block)
{
let level_spec =
cx.tcx.lint_level_spec_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block);
if level_spec.is_expect() {
// Since we're going to deduplicate expanded unsafe blocks by its enclosing macro definition soon,
// which would lead to unfulfilled `#[expect()]`s in all other unsafe blocks that are filtered out
// except for the one we emit the warning at, we must manually fulfill the lint
// for all unsafe blocks here.
cx.fulfill_expectation(id);
// `unwrap` is safe because `Expect` lints always have a `lint_id`.
cx.fulfill_expectation(level_spec.lint_id().unwrap());
}
})
.map(|id| {
@@ -2,7 +2,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use rustc_ast::ast::{self, Inline, ItemKind, ModKind};
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_session::impl_lint_pass;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::{FileName, Ident, SourceFile, Span, SyntaxContext, sym};
@@ -137,9 +137,9 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow
&& cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow
&& cx.builder.lint_level(INLINE_MODULES).level == Level::Allow
if cx.builder.lint_level_spec(MOD_MODULE_FILES).is_allow()
&& cx.builder.lint_level_spec(SELF_NAMED_MODULE_FILES).is_allow()
&& cx.builder.lint_level_spec(INLINE_MODULES).is_allow()
{
return;
}
@@ -192,9 +192,9 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
}
fn check_item_post(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow
&& cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow
&& cx.builder.lint_level(INLINE_MODULES).level == Level::Allow
if cx.builder.lint_level_spec(MOD_MODULE_FILES).is_allow()
&& cx.builder.lint_level_spec(SELF_NAMED_MODULE_FILES).is_allow()
&& cx.builder.lint_level_spec(INLINE_MODULES).is_allow()
{
return;
}
@@ -132,7 +132,7 @@ fn check_raw_string(&self, cx: &EarlyContext<'_>, str: &str, lit_span: Span, pre
);
},
);
if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS).level, rustc_lint::Allow) {
if !cx.get_lint_level_spec(NEEDLESS_RAW_STRINGS).is_allow() {
return;
}
}
@@ -181,7 +181,7 @@ fn check_final_expr<'tcx>(
match cx.tcx.hir_attrs(expr.hir_id) {
[] => {},
[attr] => {
if matches!(Level::from_attr(attr.name(), || attr.id()), Some((Level::Expect, _)))
if matches!(Level::from_opt_symbol(attr.name()), Some(Level::Expect))
&& let metas = attr.meta_item_list()
&& let Some(lst) = metas
&& let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice()
+4 -5
View File
@@ -104,7 +104,6 @@
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::nested_filter;
use rustc_middle::hir::place::PlaceBase;
use rustc_middle::lint::LevelAndSource;
use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind, PointerCoercion};
use rustc_middle::ty::layout::IntegerExt;
@@ -1663,12 +1662,12 @@ pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl I
let mut suppress_lint = false;
for id in ids {
let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
if let Some(expectation) = lint_id {
let level_spec = cx.tcx.lint_level_spec_at_node(lint, id);
if let Some(expectation) = level_spec.lint_id() {
cx.fulfill_expectation(expectation);
}
match level {
match level_spec.level() {
Level::Allow | Level::Expect => suppress_lint = true,
Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
}
@@ -1685,7 +1684,7 @@ pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl I
/// make sure to use `span_lint_hir` functions to emit the lint. This ensures that
/// expectations at the checked nodes will be fulfilled.
pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
cx.tcx.lint_level_spec_at_node(lint, id).is_allow()
}
pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {