use std::cmp;
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_errors::{Diagnostic, DiagnosticId, LintDiagnosticBuilder, MultiSpan};
use rustc_hir::HirId;
-use rustc_index::vec::IndexVec;
-use rustc_query_system::ich::StableHashingContext;
use rustc_session::lint::{
builtin::{self, FORBIDDEN_LINT_GROUPS},
- FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId,
+ FutureIncompatibilityReason, Level, Lint, LintId,
};
use rustc_session::Session;
use rustc_span::hygiene::MacroKind;
span: Span,
/// RFC 2383 reason
reason: Option<Symbol>,
- /// The lint tool. (e.g. rustdoc, clippy)
- tool: Option<Symbol>,
},
/// Lint level was set by a command-line flag.
/// A tuple of a lint level and its source.
pub type LevelAndSource = (Level, LintLevelSource);
-#[derive(Debug, HashStable)]
-pub struct LintLevelSets {
- pub list: IndexVec<LintStackIndex, LintSet>,
-}
-
-rustc_index::newtype_index! {
- #[derive(HashStable)]
- pub struct LintStackIndex {
- const COMMAND_LINE = 0,
- }
-}
-
-#[derive(Debug, HashStable)]
-pub struct LintSet {
- // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
- // flag.
+/// Return type for the `shallow_lint_levels_on` query.
+///
+/// This map represents the set of allowed lints and allowance levels given
+/// by the attributes for *a single HirId*.
+#[derive(Default, Debug, HashStable)]
+pub struct ShallowLintLevelMap {
pub specs: FxHashMap<LintId, LevelAndSource>,
-
- pub parent: LintStackIndex,
}
-impl LintLevelSets {
- pub fn new() -> Self {
- LintLevelSets { list: IndexVec::new() }
- }
-
- pub fn actual_level(
- level: Option<Level>,
- src: &mut LintLevelSource,
- sess: &Session,
- lint: &'static Lint,
- get_lint_id_level: impl FnOnce(LintId) -> (Option<Level>, LintLevelSource),
- ) -> Level {
- // If `level` is none then we actually assume the default level for this
- // lint.
- let mut level = level.unwrap_or_else(|| lint.default_level(sess.edition()));
-
- // If we're about to issue a warning, check at the last minute for any
- // directives against the warnings "lint". If, for example, there's an
- // `allow(warnings)` in scope then we want to respect that instead.
- //
- // We exempt `FORBIDDEN_LINT_GROUPS` from this because it specifically
- // triggers in cases (like #80988) where you have `forbid(warnings)`,
- // and so if we turned that into an error, it'd defeat the purpose of the
- // future compatibility warning.
- if level == Level::Warn && LintId::of(lint) != LintId::of(FORBIDDEN_LINT_GROUPS) {
- let (warnings_level, warnings_src) = get_lint_id_level(LintId::of(builtin::WARNINGS));
- if let Some(configured_warning_level) = warnings_level {
- if configured_warning_level != Level::Warn {
- level = configured_warning_level;
- *src = warnings_src;
- }
+/// From an initial level and source, 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(
+ level: Option<Level>,
+ src: &mut LintLevelSource,
+ sess: &Session,
+ lint: LintId,
+ probe_for_lint_level: impl FnOnce(LintId) -> (Option<Level>, LintLevelSource),
+) -> Level {
+ // If `level` is none then we actually assume the default level for this lint.
+ let mut level = level.unwrap_or_else(|| lint.lint.default_level(sess.edition()));
+
+ // If we're about to issue a warning, check at the last minute for any
+ // directives against the warnings "lint". If, for example, there's an
+ // `allow(warnings)` in scope then we want to respect that instead.
+ //
+ // We exempt `FORBIDDEN_LINT_GROUPS` from this because it specifically
+ // triggers in cases (like #80988) where you have `forbid(warnings)`,
+ // and so if we turned that into an error, it'd defeat the purpose of the
+ // future compatibility warning.
+ if level == Level::Warn && lint != LintId::of(FORBIDDEN_LINT_GROUPS) {
+ let (warnings_level, warnings_src) = probe_for_lint_level(LintId::of(builtin::WARNINGS));
+ if let Some(configured_warning_level) = warnings_level {
+ if configured_warning_level != Level::Warn {
+ level = configured_warning_level;
+ *src = warnings_src;
}
}
-
- // Ensure that we never exceed the `--cap-lints` argument
- // unless the source is a --force-warn
- level = if let LintLevelSource::CommandLine(_, Level::ForceWarn(_)) = src {
- level
- } else {
- cmp::min(level, sess.opts.lint_cap.unwrap_or(Level::Forbid))
- };
-
- if let Some(driver_level) = sess.driver_lint_caps.get(&LintId::of(lint)) {
- // Ensure that we never exceed driver level.
- level = cmp::min(*driver_level, level);
- }
-
- level
}
- pub fn get_lint_level(
- &self,
- lint: &'static Lint,
- idx: LintStackIndex,
- aux: Option<&FxHashMap<LintId, LevelAndSource>>,
- sess: &Session,
- ) -> LevelAndSource {
- let (level, mut src) = self.get_lint_id_level(LintId::of(lint), idx, aux);
-
- let level = Self::actual_level(level, &mut src, sess, lint, |id| {
- self.get_lint_id_level(id, idx, aux)
- });
+ // Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn
+ level = if let LintLevelSource::CommandLine(_, Level::ForceWarn(_)) = src {
+ level
+ } else {
+ cmp::min(level, sess.opts.lint_cap.unwrap_or(Level::Forbid))
+ };
- (level, src)
+ if let Some(driver_level) = sess.driver_lint_caps.get(&lint) {
+ // Ensure that we never exceed driver level.
+ level = cmp::min(*driver_level, level);
}
- pub fn get_lint_id_level(
+ level
+}
+
+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
+ /// `reveal_actual_level` beforehand.
+ fn probe_for_lint_level(
&self,
+ tcx: TyCtxt<'_>,
id: LintId,
- mut idx: LintStackIndex,
- aux: Option<&FxHashMap<LintId, LevelAndSource>>,
+ start: HirId,
) -> (Option<Level>, LintLevelSource) {
- if let Some(specs) = aux {
- if let Some(&(level, src)) = specs.get(&id) {
- return (Some(level), src);
- }
+ if let Some(&(level, src)) = self.specs.get(&id) {
+ return (Some(level), src);
}
- loop {
- let LintSet { ref specs, parent } = self.list[idx];
- if let Some(&(level, src)) = specs.get(&id) {
+
+ for (parent, _) in tcx.hir().parent_iter(start) {
+ let specs = tcx.shallow_lint_levels_on(parent);
+ if let Some(&(level, src)) = specs.specs.get(&id) {
return (Some(level), src);
}
- if idx == COMMAND_LINE {
- return (None, LintLevelSource::Default);
- }
- idx = parent;
}
+ (None, LintLevelSource::Default)
}
-}
-
-#[derive(Debug)]
-pub struct LintLevelMap {
- /// This is a collection of lint expectations as described in RFC 2383, that
- /// can be fulfilled during this compilation session. This means that at least
- /// one expected lint is currently registered in the lint store.
- ///
- /// The [`LintExpectationId`] is stored as a part of the [`Expect`](Level::Expect)
- /// lint level.
- pub lint_expectations: Vec<(LintExpectationId, LintExpectation)>,
- pub sets: LintLevelSets,
- pub id_to_set: FxHashMap<HirId, LintStackIndex>,
-}
-impl LintLevelMap {
- /// If the `id` was previously registered with `register_id` when building
- /// this `LintLevelMap` this returns the corresponding lint level and source
- /// of the lint level for the lint provided.
- ///
- /// If the `id` was not previously registered, returns `None`. If `None` is
- /// returned then the parent of `id` should be acquired and this function
- /// should be called again.
- pub fn level_and_source(
+ /// Fetch and return the user-visible lint level for the given lint at the given HirId.
+ pub fn lint_level_id_at_node(
&self,
- lint: &'static Lint,
+ tcx: TyCtxt<'_>,
+ lint: LintId,
id: HirId,
- session: &Session,
- ) -> Option<LevelAndSource> {
- self.id_to_set.get(&id).map(|idx| self.sets.get_lint_level(lint, *idx, None, session))
- }
-}
-
-impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
- #[inline]
- fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
- let LintLevelMap { ref sets, ref id_to_set, ref lint_expectations } = *self;
-
- id_to_set.hash_stable(hcx, hasher);
- lint_expectations.hash_stable(hcx, hasher);
-
- hcx.while_hashing_spans(true, |hcx| sets.hash_stable(hcx, hasher))
+ ) -> (Level, LintLevelSource) {
+ let (level, mut src) = self.probe_for_lint_level(tcx, lint, id);
+ let level = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| {
+ self.probe_for_lint_level(tcx, lint, id)
+ });
+ debug!(?id, ?level, ?src);
+ (level, src)
}
}
-pub struct LintLevelQueryMap<'tcx> {
- pub tcx: TyCtxt<'tcx>,
- pub cur: HirId,
- pub specs: FxHashMap<LintId, LevelAndSource>,
-}
-
-impl<'tcx> LintLevelQueryMap<'tcx> {
- pub fn lint_id_level(&self, id: LintId) -> (Option<Level>, LintLevelSource) {
- Self::get_lint_id_level(id, self.cur, self.tcx, &self.specs)
- }
- pub fn lint_level(&self, lint: &'static Lint) -> LevelAndSource {
- Self::get_lint_level(LintId::of(lint), self.cur, self.tcx, &self.specs)
+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) -> (Level, LintLevelSource) {
+ self.shallow_lint_levels_on(id).lint_level_id_at_node(self, LintId::of(lint), id)
}
- pub fn get_lint_id_level(
- id: LintId,
- cur: HirId,
- tcx: TyCtxt<'tcx>,
- specs: &FxHashMap<LintId, LevelAndSource>,
- ) -> (Option<Level>, LintLevelSource) {
- if let Some(&(level, src)) = specs.get(&id) {
- return (Some(level), src);
+ /// Walks upwards from `id` to find a node which might change lint levels with attributes.
+ /// It stops at `bound` and just returns it if reached.
+ pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId {
+ let hir = self.hir();
+ while id != bound && self.shallow_lint_levels_on(id).specs.is_empty() {
+ id = hir.get_parent_node(id)
}
- let mut cur = cur;
-
- loop {
- let parent = tcx.hir().get_parent_node(cur);
- if cur == parent {
- return (None, LintLevelSource::Default);
- }
- let specs = tcx.lint_levels_on(parent);
- if let Some(&(level, src)) = specs.get(&id) {
- return (Some(level), src);
- }
- cur = parent
- }
- }
-
- pub fn get_lint_level(
- id: LintId,
- cur: HirId,
- tcx: TyCtxt<'tcx>,
- specs: &FxHashMap<LintId, LevelAndSource>,
- ) -> (Level, LintLevelSource) {
- let (level, mut src) = Self::get_lint_id_level(id, cur, tcx, specs);
- let level = LintLevelSets::actual_level(level, &mut src, tcx.sess, id.lint, |id| {
- Self::get_lint_id_level(id, cur, tcx, specs)
- });
- (level, src)
+ id
}
}