compiler/rustc_passes/src/stability.rs RUST 1,396 lines View on github.com → Search inside
1//! A pass that annotates every item and method with its stability level,2//! propagating default levels lexically from parent to children ast nodes.34use std::num::NonZero;56use rustc_ast_lowering::stability::extern_abi_stability;7use rustc_attr_ir::{8    AttributeKind, ConstStability, DefaultBodyStability, DeprecatedSince, Stability,9    StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER, find_attr,10};11use rustc_data_structures::fx::FxIndexMap;12use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};13use rustc_feature::{EnabledLangFeature, EnabledLibFeature, UNSTABLE_LANG_FEATURES};14use rustc_hir::def::{DefKind, Res};15use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModId};16use rustc_hir::intravisit::{self, Visitor};17use rustc_hir::{18    self as hir, AmbigArg, Constness, FieldDef, HirId, Item, ItemKind, Path, TraitRef, Ty, TyKind,19    UsePath, Variant,20};21use rustc_lint_defs::builtin::{22    DUPLICATE_FEATURES, INEFFECTIVE_UNSTABLE_REEXPORTS, INEFFECTIVE_UNSTABLE_TRAIT_IMPL,23    STABLE_FEATURES, UNSTABLE_IMPORTS,24};25use rustc_middle::hir::nested_filter;26use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures};27use rustc_middle::middle::privacy::EffectiveVisibilities;28use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, EvalResult};29use rustc_middle::query::{LocalCrate, Providers};30use rustc_middle::ty::{AssocContainer, TyCtxt};31use rustc_span::{Span, Symbol, span_bug, sym};32use tracing::instrument;3334use crate::diagnostics;3536#[derive(PartialEq)]37enum AnnotationKind {38    /// Annotation is required if not inherited from unstable parents.39    Required,40    /// Annotation is useless, reject it.41    Prohibited,42    /// Deprecation annotation is useless, reject it. (Stability attribute is still required.)43    DeprecationProhibited,44    /// Annotation itself is useless, but it can be propagated to children.45    Container,46}4748fn inherit_deprecation(def_kind: DefKind) -> bool {49    match def_kind {50        DefKind::LifetimeParam | DefKind::TyParam | DefKind::ConstParam => false,51        _ => true,52    }53}5455fn inherit_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {56    let def_kind = tcx.def_kind(def_id);57    match def_kind {58        DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {59            match tcx.def_kind(tcx.local_parent(def_id)) {60                DefKind::Trait | DefKind::Impl { .. } => true,61                _ => false,62            }63        }64        DefKind::Closure => true,65        _ => false,66    }67}6869fn annotation_kind(tcx: TyCtxt<'_>, def_id: LocalDefId) -> AnnotationKind {70    let def_kind = tcx.def_kind(def_id);71    match def_kind {72        // Inherent impls and foreign modules serve only as containers for other items,73        // they don't have their own stability. They still can be annotated as unstable74        // and propagate this unstability to children, but this annotation is completely75        // optional. They inherit stability from their parents when unannotated.76        DefKind::Impl { of_trait: false } | DefKind::ForeignMod => AnnotationKind::Container,77        DefKind::Impl { of_trait: true } => AnnotationKind::DeprecationProhibited,7879        // Allow stability attributes on default generic arguments.80        DefKind::TyParam | DefKind::ConstParam => {81            match &tcx.hir_node_by_def_id(def_id).expect_generic_param().kind {82                hir::GenericParamKind::Type { default: Some(_), .. }83                | hir::GenericParamKind::Const { default: Some(_), .. } => {84                    AnnotationKind::Container85                }86                _ => AnnotationKind::Prohibited,87            }88        }8990        // Impl items in trait impls cannot have stability.91        DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst => {92            match tcx.def_kind(tcx.local_parent(def_id)) {93                DefKind::Impl { of_trait: true } => AnnotationKind::Prohibited,94                _ => AnnotationKind::Required,95            }96        }9798        _ => AnnotationKind::Required,99    }100}101102fn lookup_deprecation_entry(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DeprecationEntry> {103    let depr = find_attr!(tcx, def_id,104        Deprecated { deprecation, span: _ } => *deprecation105    );106107    let Some(depr) = depr else {108        if inherit_deprecation(tcx.def_kind(def_id)) {109            let parent_id = tcx.opt_local_parent(def_id)?;110            let parent_depr = tcx.lookup_deprecation_entry(parent_id)?;111            return Some(parent_depr);112        }113114        return None;115    };116117    // `Deprecation` is just two pointers, no need to intern it118    Some(DeprecationEntry::local(depr, def_id))119}120121fn inherit_stability(def_kind: DefKind) -> bool {122    match def_kind {123        DefKind::Field | DefKind::Variant | DefKind::Ctor(..) => true,124        _ => false,125    }126}127128/// If the `-Z force-unstable-if-unmarked` flag is passed then we provide129/// a parent stability annotation which indicates that this is private130/// with the `rustc_private` feature. This is intended for use when131/// compiling library and `rustc_*` crates themselves so we can leverage crates.io132/// while maintaining the invariant that all sysroot crates are unstable133/// by default and are unable to be used.134const FORCE_UNSTABLE: Stability = Stability {135    level: StabilityLevel::Unstable {136        reason: UnstableReason::Default,137        issue: NonZero::new(27812),138        implied_by: None,139        old_name: None,140    },141    feature: sym::rustc_private,142};143144#[instrument(level = "debug", skip(tcx))]145fn lookup_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Stability> {146    // Propagate unstability. This can happen even for non-staged-api crates in case147    // -Zforce-unstable-if-unmarked is set.148    if !tcx.features().staged_api() {149        if !tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {150            return None;151        }152153        let Some(parent) = tcx.opt_local_parent(def_id) else { return Some(FORCE_UNSTABLE) };154155        if inherit_deprecation(tcx.def_kind(def_id)) {156            let parent = tcx.lookup_stability(parent)?;157            if parent.is_unstable() {158                return Some(parent);159            }160        }161162        return None;163    }164165    // # Regular stability166    let stab = find_attr!(tcx, def_id, Stability { stability, span: _ } => *stability);167168    if let Some(stab) = stab {169        return Some(stab);170    }171172    if inherit_deprecation(tcx.def_kind(def_id)) {173        let Some(parent) = tcx.opt_local_parent(def_id) else {174            return tcx175                .sess176                .opts177                .unstable_opts178                .force_unstable_if_unmarked179                .then_some(FORCE_UNSTABLE);180        };181        let parent = tcx.lookup_stability(parent)?;182        if parent.is_unstable() || inherit_stability(tcx.def_kind(def_id)) {183            return Some(parent);184        }185    }186187    None188}189190#[instrument(level = "debug", skip(tcx))]191fn lookup_default_body_stability(192    tcx: TyCtxt<'_>,193    def_id: LocalDefId,194) -> Option<DefaultBodyStability> {195    if !tcx.features().staged_api() {196        return None;197    }198199    // FIXME: check that this item can have body stability200    find_attr!(tcx, def_id, RustcBodyStability { stability, .. } => *stability)201}202203#[instrument(level = "debug", skip(tcx))]204fn lookup_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ConstStability> {205    if !tcx.features().staged_api() {206        // Propagate unstability. This can happen even for non-staged-api crates in case207        // -Zforce-unstable-if-unmarked is set.208        if inherit_deprecation(tcx.def_kind(def_id)) {209            let parent = tcx.opt_local_parent(def_id)?;210            let parent_stab = tcx.lookup_stability(parent)?;211            if parent_stab.is_unstable()212                && let Some(fn_sig) = tcx.hir_node_by_def_id(def_id).fn_sig()213                && matches!(fn_sig.header.constness, Constness::Const { .. })214            {215                let const_stable_indirect = find_attr!(tcx, def_id, RustcConstStableIndirect);216                return Some(ConstStability::unmarked(const_stable_indirect, parent_stab));217            }218        }219220        return None;221    }222223    let const_stable_indirect = find_attr!(tcx, def_id, RustcConstStableIndirect);224    let const_stab =225        find_attr!(tcx, def_id, RustcConstStability { stability, span: _ } => *stability);226227    // After checking the immediate attributes, get rid of the span and compute implied228    // const stability: inherit feature gate from regular stability.229    let mut const_stab = const_stab230        .map(|const_stab| ConstStability::from_partial(const_stab, const_stable_indirect));231232    // If this is a const fn but not annotated with stability markers, see if we can inherit233    // regular stability.234    if let Some(fn_sig) = tcx.hir_node_by_def_id(def_id).fn_sig()235        && matches!(fn_sig.header.constness, Constness::Const { .. })236        && const_stab.is_none()237        // We only ever inherit unstable features.238        && let Some(inherit_regular_stab) = tcx.lookup_stability(def_id)239        && inherit_regular_stab.is_unstable()240    {241        const_stab = Some(ConstStability {242            // We subject these implicitly-const functions to recursive const stability.243            const_stable_indirect: true,244            promotable: false,245            level: inherit_regular_stab.level,246            feature: inherit_regular_stab.feature,247        });248    }249250    if let Some(const_stab) = const_stab {251        return Some(const_stab);252    }253254    // `impl const Trait for Type` items forward their const stability to their immediate children.255    // FIXME(const_trait_impl): how is this supposed to interact with `#[rustc_const_stable_indirect]`?256    // Currently, once that is set, we do not inherit anything from the parent any more.257    if inherit_const_stability(tcx, def_id) {258        let parent = tcx.opt_local_parent(def_id)?;259        let parent = tcx.lookup_const_stability(parent)?;260        if parent.is_const_unstable() {261            return Some(parent);262        }263    }264265    None266}267268fn stability_implications(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> UnordMap<Symbol, Symbol> {269    let mut implications = UnordMap::default();270271    let mut register_implication = |def_id| {272        if let Some(stability) = tcx.lookup_stability(def_id)273            && let StabilityLevel::Unstable { implied_by: Some(implied_by), .. } = stability.level274        {275            implications.insert(implied_by, stability.feature);276        }277278        if let Some(stability) = tcx.lookup_const_stability(def_id)279            && let StabilityLevel::Unstable { implied_by: Some(implied_by), .. } = stability.level280        {281            implications.insert(implied_by, stability.feature);282        }283    };284285    if tcx.features().staged_api() {286        register_implication(CRATE_DEF_ID);287        for def_id in tcx.hir_crate_items(()).definitions() {288            register_implication(def_id);289            let def_kind = tcx.def_kind(def_id);290            if def_kind.is_adt() {291                let adt = tcx.adt_def(def_id);292                for variant in adt.variants() {293                    if variant.def_id != def_id.to_def_id() {294                        register_implication(variant.def_id.expect_local());295                    }296                    for field in &variant.fields {297                        register_implication(field.did.expect_local());298                    }299                    if let Some(ctor_def_id) = variant.ctor_def_id() {300                        register_implication(ctor_def_id.expect_local())301                    }302                }303            }304            if def_kind.has_generics() {305                for param in tcx.generics_of(def_id).own_params.iter() {306                    register_implication(param.def_id.expect_local())307                }308            }309        }310    }311312    implications313}314315struct MissingStabilityAnnotations<'tcx> {316    tcx: TyCtxt<'tcx>,317    effective_visibilities: &'tcx EffectiveVisibilities,318}319320impl<'tcx> MissingStabilityAnnotations<'tcx> {321    /// Verify that deprecation and stability attributes make sense with one another.322    #[instrument(level = "trace", skip(self))]323    fn check_compatible_stability(&self, def_id: LocalDefId) {324        if !self.tcx.features().staged_api() {325            return;326        }327328        let depr = self.tcx.lookup_deprecation_entry(def_id);329        let stab = self.tcx.lookup_stability(def_id);330        let const_stab = self.tcx.lookup_const_stability(def_id);331332        macro_rules! find_attr_span {333            ($name:ident) => {{334                let attrs = self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id(def_id));335                find_attr!(attrs, AttributeKind::$name { span, .. } => *span)336            }}337        }338339        if stab.is_none()340            && depr.is_some_and(|d| d.attr.is_since_rustc_version())341            && let Some(span) = find_attr_span!(Deprecated)342        {343            self.tcx.dcx().emit_err(diagnostics::DeprecatedAttribute { span });344        }345346        if let Some(stab) = stab {347            // Error if prohibited, or can't inherit anything from a container.348            let kind = annotation_kind(self.tcx, def_id);349            if kind == AnnotationKind::Prohibited350                || (kind == AnnotationKind::Container && stab.level.is_stable() && depr.is_some())351            {352                if let Some(span) = find_attr_span!(Stability) {353                    let item_sp = self.tcx.def_span(def_id);354                    self.tcx.dcx().emit_err(diagnostics::UselessStability { span, item_sp });355                }356            }357358            // Check if deprecated_since < stable_since. If it is,359            // this is *almost surely* an accident.360            if let Some(depr) = depr361                && let DeprecatedSince::RustcVersion(dep_since) = depr.attr.since362                && let StabilityLevel::Stable { since: stab_since, .. } = stab.level363                && let Some(span) = find_attr_span!(Stability)364            {365                let item_sp = self.tcx.def_span(def_id);366                match stab_since {367                    StableSince::Current => {368                        self.tcx369                            .dcx()370                            .emit_err(diagnostics::CannotStabilizeDeprecated { span, item_sp });371                    }372                    StableSince::Version(stab_since) => {373                        if dep_since < stab_since {374                            self.tcx375                                .dcx()376                                .emit_err(diagnostics::CannotStabilizeDeprecated { span, item_sp });377                        }378                    }379                    StableSince::Err(_) => {380                        // An error already reported. Assume the unparseable stabilization381                        // version is older than the deprecation version.382                    }383                }384            }385        }386387        // If the current node is a function with const stability attributes (directly given or388        // implied), check if the function/method is const or the parent impl block is const.389        let fn_sig = self.tcx.hir_node_by_def_id(def_id).fn_sig();390        if let Some(fn_sig) = fn_sig391            && !matches!(fn_sig.header.constness, Constness::Const { .. })392            && const_stab.is_some()393            && find_attr_span!(RustcConstStability).is_some()394        {395            self.tcx.dcx().emit_err(diagnostics::MissingConstErr { fn_sig_span: fn_sig.span });396        }397398        // If this is marked const *stable*, it must also be regular-stable.399        if let Some(const_stab) = const_stab400            && let Some(fn_sig) = fn_sig401            && const_stab.is_const_stable()402            && !stab.is_some_and(|s| s.is_stable())403            && let Some(path_span) = find_attr_span!(RustcConstStability)404        {405            self.tcx.dcx().emit_err(diagnostics::ConstStableNotStable {406                fn_sig_span: fn_sig.span,407                path_span,408            });409        }410411        if let Some(stab) = &const_stab412            && stab.is_const_stable()413            && stab.const_stable_indirect414            && let Some(span) = find_attr_span!(RustcConstStability)415        {416            self.tcx.dcx().emit_err(diagnostics::RustcConstStableIndirectPairing { span });417        }418    }419420    #[instrument(level = "debug", skip(self))]421    fn check_missing_stability(&self, def_id: LocalDefId) {422        let stab = self.tcx.lookup_stability(def_id);423        self.tcx.ensure_ok().lookup_const_stability(def_id);424        if !self.tcx.sess.is_test_crate()425            && stab.is_none()426            && self.effective_visibilities.is_reachable(def_id)427        {428            let descr = self.tcx.def_descr(def_id.to_def_id());429            let span = self.tcx.def_span(def_id);430            self.tcx.dcx().emit_err(diagnostics::MissingStabilityAttr { span, descr });431        }432    }433434    fn check_missing_const_stability(&self, def_id: LocalDefId) {435        let is_const = self.tcx.is_const_fn(def_id.to_def_id())436            || (self.tcx.def_kind(def_id.to_def_id()) == DefKind::Trait437                && self.tcx.is_const_trait(def_id.to_def_id()));438439        // Reachable const fn/trait must have a stability attribute.440        if is_const441            && self.effective_visibilities.is_reachable(def_id)442            && self.tcx.lookup_const_stability(def_id).is_none()443        {444            let span = self.tcx.def_span(def_id);445            let descr = self.tcx.def_descr(def_id.to_def_id());446            self.tcx.dcx().emit_err(diagnostics::MissingConstStabAttr { span, descr });447        }448    }449}450451impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {452    type NestedFilter = nested_filter::OnlyBodies;453454    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {455        self.tcx456    }457458    fn visit_item(&mut self, i: &'tcx Item<'tcx>) {459        self.check_compatible_stability(i.owner_id.def_id);460461        // Inherent impls and foreign modules serve only as containers for other items,462        // they don't have their own stability. They still can be annotated as unstable463        // and propagate this instability to children, but this annotation is completely464        // optional. They inherit stability from their parents when unannotated.465        if !matches!(466            i.kind,467            hir::ItemKind::Impl(hir::Impl { of_trait: None, .. })468                | hir::ItemKind::ForeignMod { .. }469        ) {470            self.check_missing_stability(i.owner_id.def_id);471        }472473        // Ensure stable `const fn` have a const stability attribute.474        self.check_missing_const_stability(i.owner_id.def_id);475476        intravisit::walk_item(self, i)477    }478479    fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {480        self.check_compatible_stability(ti.owner_id.def_id);481        self.check_missing_stability(ti.owner_id.def_id);482        intravisit::walk_trait_item(self, ti);483    }484485    fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {486        self.check_compatible_stability(ii.owner_id.def_id);487        if let hir::ImplItemImplKind::Inherent { .. } = ii.impl_kind {488            self.check_missing_stability(ii.owner_id.def_id);489            self.check_missing_const_stability(ii.owner_id.def_id);490        }491        intravisit::walk_impl_item(self, ii);492    }493494    fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) {495        self.check_compatible_stability(var.def_id);496        self.check_missing_stability(var.def_id);497        if let Some(ctor_def_id) = var.data.ctor_def_id() {498            self.check_missing_stability(ctor_def_id);499        }500        intravisit::walk_variant(self, var);501    }502503    fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {504        self.check_compatible_stability(s.def_id);505        self.check_missing_stability(s.def_id);506        intravisit::walk_field_def(self, s);507    }508509    fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {510        self.check_compatible_stability(i.owner_id.def_id);511        self.check_missing_stability(i.owner_id.def_id);512        intravisit::walk_foreign_item(self, i);513    }514515    fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {516        self.check_compatible_stability(p.def_id);517        // Note that we don't need to `check_missing_stability` for default generic parameters,518        // as we assume that any default generic parameters without attributes are automatically519        // stable (assuming they have not inherited instability from their parent).520        intravisit::walk_generic_param(self, p);521    }522}523524/// Cross-references the feature names of unstable APIs with enabled525/// features and possibly prints errors.526fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, mod_id: LocalModId) {527    let mut checker = Checker { tcx, mod_id, unstable_reexports: FxIndexMap::default() };528    tcx.hir_visit_item_likes_in_module(mod_id, &mut checker);529    checker.emit_ineffective_unstable_reexports();530531    let is_staged_api =532        tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api();533    if is_staged_api {534        let effective_visibilities = &tcx.effective_visibilities(());535        let mut missing = MissingStabilityAnnotations { tcx, effective_visibilities };536        if mod_id.is_top_level_module() {537            missing.check_missing_stability(CRATE_DEF_ID);538        }539        tcx.hir_visit_item_likes_in_module(mod_id, &mut missing);540    }541542    if mod_id.is_top_level_module() {543        check_unused_or_stable_features(tcx)544    }545}546547pub(crate) fn provide(providers: &mut Providers) {548    *providers = Providers {549        check_mod_unstable_api_usage,550        stability_implications,551        lookup_stability,552        lookup_const_stability,553        lookup_default_body_stability,554        lookup_deprecation_entry,555        ..*providers556    };557}558559struct UnstableReexport {560    hir_id: HirId,561    span: Span,562    has_target: bool,563    all_targets_stable: bool,564}565566struct Checker<'tcx> {567    tcx: TyCtxt<'tcx>,568    mod_id: LocalModId,569    unstable_reexports: FxIndexMap<Span, UnstableReexport>,570}571572impl<'tcx> Checker<'tcx> {573    fn unstable_reexport_span(&self, hir_id: HirId) -> Option<Span> {574        let attrs = self.tcx.hir_attrs(hir_id);575        let (stability, span) =576            find_attr!(attrs, Stability { stability, span } => (*stability, *span))?;577578        stability.level.is_unstable().then_some(span)579    }580581    fn classify_reexport_targets<Id>(582        &self,583        targets: impl IntoIterator<Item = Res<Id>>,584    ) -> (bool, bool) {585        let mut has_target = false;586        let mut all_targets_stable = true;587588        for res in targets {589            match res {590                Res::Def(_, def_id) => {591                    has_target = true;592593                    match self.tcx.lookup_stability(def_id) {594                        Some(stability) if stability.level.is_unstable() => {595                            all_targets_stable = false;596                        }597                        Some(_) => {}598599                        None => {600                            // Items from crates without staged API metadata are601                            // effectively stable. Unmarked items in staged API602                            // crates are diagnosed by the existing stability checks.603                            if self.tcx.lookup_stability(def_id.krate.as_def_id()).is_some() {604                                all_targets_stable = false;605                            }606                        }607                    }608                }609610                // Primitives are stable and have no DefId.611                Res::PrimTy(_) => {612                    has_target = true;613                }614615                // Do not lint if the target cannot be classified.616                _ => {617                    all_targets_stable = false;618                }619            }620        }621622        (has_target, all_targets_stable)623    }624625    fn record_unstable_reexport(626        &mut self,627        hir_id: HirId,628        attr_span: Span,629        span: Span,630        has_target: bool,631        all_targets_stable: bool,632    ) {633        let entry = self.unstable_reexports.entry(attr_span).or_insert(UnstableReexport {634            hir_id,635            span,636            has_target: false,637            all_targets_stable: true,638        });639640        entry.has_target |= has_target;641        entry.all_targets_stable &= all_targets_stable;642    }643644    fn check_single_unstable_reexport(&mut self, hir_id: HirId, path: &'tcx UsePath<'tcx>) {645        let Some(attr_span) = self.unstable_reexport_span(hir_id) else {646            return;647        };648649        let (has_target, all_targets_stable) =650            self.classify_reexport_targets(path.res.present_items());651652        self.record_unstable_reexport(hir_id, attr_span, path.span, has_target, all_targets_stable);653    }654655    fn check_glob_unstable_reexport(656        &mut self,657        hir_id: HirId,658        glob_def_id: LocalDefId,659        path: &'tcx UsePath<'tcx>,660    ) {661        let Some(attr_span) = self.unstable_reexport_span(hir_id) else {662            return;663        };664665        let glob_def_id = glob_def_id.to_def_id();666667        let targets = self668            .tcx669            .module_children_local(self.mod_id.to_local_def_id())670            .iter()671            .filter(|child| {672                child.reexport_chain.iter().any(|reexport| reexport.id() == Some(glob_def_id))673            })674            .map(|child| child.res);675676        let (has_target, all_targets_stable) = self.classify_reexport_targets(targets);677678        self.record_unstable_reexport(hir_id, attr_span, path.span, has_target, all_targets_stable);679    }680681    fn containing_module_is_unstable(&self) -> bool {682        self.tcx683            .lookup_stability(self.mod_id.to_local_def_id())684            .is_some_and(|stability| stability.level.is_unstable())685    }686687    fn emit_ineffective_unstable_reexports(&self) {688        // an unstable module already makes its re-exports unstable689        // keep the explicit annotation without linting it as ineffective690        if self.unstable_reexports.is_empty() || self.containing_module_is_unstable() {691            return;692        }693694        for reexport in self.unstable_reexports.values() {695            if reexport.has_target && reexport.all_targets_stable {696                self.tcx.emit_node_span_lint(697                    INEFFECTIVE_UNSTABLE_REEXPORTS,698                    reexport.hir_id,699                    reexport.span,700                    diagnostics::IneffectiveUnstableReexport,701                );702            }703        }704    }705}706707impl<'tcx> Visitor<'tcx> for Checker<'tcx> {708    type NestedFilter = nested_filter::OnlyBodies;709710    /// Because stability levels are scoped lexically, we want to walk711    /// nested items in the context of the outer item, so enable712    /// deep-walking.713    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {714        self.tcx715    }716717    fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {718        match item.kind {719            hir::ItemKind::ExternCrate(_, ident) => {720                // compiler-generated `extern crate` items have a dummy span.721                // `std` is still checked for the `restricted-std` feature.722                if item.span.is_dummy() && ident.name != sym::std {723                    return;724                }725726                let Some(cnum) = self.tcx.extern_mod_stmt_cnum(item.owner_id.def_id) else {727                    return;728                };729                let def_id = cnum.as_def_id();730                self.tcx.check_stability(def_id, Some(item.hir_id()), item.span, None);731            }732733            // For implementations of traits, check the stability of each item734            // individually as it's possible to have a stable trait with unstable735            // items.736            hir::ItemKind::Impl(hir::Impl {737                of_trait: Some(of_trait),738                self_ty,739                items,740                constness,741                ..742            }) => {743                let features = self.tcx.features();744                if features.staged_api() {745                    let attrs = self.tcx.hir_attrs(item.hir_id());746                    let stab = find_attr!(attrs, Stability{stability, span} => (*stability, *span));747748                    // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem749                    let const_stab =750                        find_attr!(attrs, RustcConstStability{stability, ..} => *stability);751752                    let unstable_feature_stab = find_attr!(attrs, UnstableFeatureBound(i) => i)753                        .map(|i| i.as_slice())754                        .unwrap_or_default();755756                    // If this impl block has an #[unstable] attribute, give an757                    // error if all involved types and traits are stable, because758                    // it will have no effect.759                    // See: https://github.com/rust-lang/rust/issues/55436760                    //761                    // The exception is when there are both  #[unstable_feature_bound(..)] and762                    //  #![unstable(feature = "..", issue = "..")] that have the same symbol because763                    // that can effectively mark an impl as unstable.764                    //765                    // For example:766                    // ```767                    // #[unstable_feature_bound(feat_foo)]768                    // #[unstable(feature = "feat_foo", issue = "none")]769                    // impl Foo for Bar {}770                    // ```771                    if let Some((772                        Stability { level: StabilityLevel::Unstable { .. }, feature },773                        span,774                    )) = stab775                    {776                        let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };777                        c.visit_ty_unambig(self_ty);778                        c.visit_trait_ref(&of_trait.trait_ref);779780                        // Skip the lint if the impl is marked as unstable using781                        // #[unstable_feature_bound(..)]782                        let mut unstable_feature_bound_in_effect = false;783                        for (unstable_bound_feat_name, _) in unstable_feature_stab {784                            if *unstable_bound_feat_name == feature {785                                unstable_feature_bound_in_effect = true;786                            }787                        }788789                        // do not lint when the trait isn't resolved, since resolution error should790                        // be fixed first791                        if of_trait.trait_ref.path.res != Res::Err792                            && c.fully_stable793                            && !unstable_feature_bound_in_effect794                        {795                            self.tcx.emit_node_span_lint(796                                INEFFECTIVE_UNSTABLE_TRAIT_IMPL,797                                item.hir_id(),798                                span,799                                diagnostics::IneffectiveUnstableImpl,800                            );801                        }802                    }803804                    if features.const_trait_impl()805                        && let hir::Constness::Const { .. } = constness806                    {807                        let stable_or_implied_stable = match const_stab {808                            None => true,809                            Some(stab) if stab.is_const_stable() => {810                                // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable811                                // needs to have an error emitted.812                                // Note: Remove this error once `const_trait_impl` is stabilized813                                self.tcx.dcx().emit_err(diagnostics::TraitImplConstStable {814                                    span: item.span,815                                });816                                true817                            }818                            Some(_) => false,819                        };820821                        if let Some(trait_id) = of_trait.trait_ref.trait_def_id()822                            && let Some(const_stab) = self.tcx.lookup_const_stability(trait_id)823                        {824                            // the const stability of a trait impl must match the const stability on the trait.825                            if const_stab.is_const_stable() != stable_or_implied_stable {826                                let trait_span = self.tcx.def_ident_span(trait_id).unwrap();827828                                let impl_stability = if stable_or_implied_stable {829                                    diagnostics::ImplConstStability::Stable { span: item.span }830                                } else {831                                    diagnostics::ImplConstStability::Unstable { span: item.span }832                                };833                                let trait_stability = if const_stab.is_const_stable() {834                                    diagnostics::TraitConstStability::Stable { span: trait_span }835                                } else {836                                    diagnostics::TraitConstStability::Unstable { span: trait_span }837                                };838839                                self.tcx.dcx().emit_err(840                                    diagnostics::TraitImplConstStabilityMismatch {841                                        span: item.span,842                                        impl_stability,843                                        trait_stability,844                                    },845                                );846                            }847                        }848                    }849                }850851                if let hir::Constness::Const { .. } = constness852                    && let Some(def_id) = of_trait.trait_ref.trait_def_id()853                {854                    // FIXME(const_trait_impl): Improve the span here.855                    self.tcx.check_const_stability(856                        def_id,857                        of_trait.trait_ref.path.span,858                        of_trait.trait_ref.path.span,859                    );860                }861862                for impl_item_ref in items {863                    let impl_item = self.tcx.associated_item(impl_item_ref.owner_id);864865                    if let AssocContainer::TraitImpl(Ok(def_id)) = impl_item.container {866                        // Pass `None` to skip deprecation warnings.867                        self.tcx.check_stability(868                            def_id,869                            None,870                            self.tcx.def_span(impl_item_ref.owner_id),871                            None,872                        );873                    }874                }875            }876877            _ => (/* pass */),878        }879        intravisit::walk_item(self, item);880    }881882    fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef<'tcx>) {883        match t.modifiers.constness {884            hir::BoundConstness::Always(span) | hir::BoundConstness::Maybe(span) => {885                if let Some(def_id) = t.trait_ref.trait_def_id() {886                    self.tcx.check_const_stability(def_id, t.trait_ref.path.span, span);887                }888            }889            hir::BoundConstness::Never => {}890        }891        intravisit::walk_poly_trait_ref(self, t);892    }893894    fn visit_use(&mut self, tree: &'tcx hir::UseTree<'tcx>, hir_id: HirId, def_id: LocalDefId) {895        let mut v = vec![];896897        #[instrument(skip(visitor))]898        fn recurse<'tcx>(899            visitor: &mut Checker<'tcx>,900            tree: &'tcx hir::UseTree<'tcx>,901            hir_id: HirId,902            def_id: LocalDefId,903            stack: &mut Vec<&'tcx [hir::PathSegment<'tcx>]>,904        ) {905            let UsePath { segments, res, span } = *tree.prefix;906907            match tree.kind {908                hir::UseKind::Single(_) | hir::UseKind::Glob => {909                    if visitor.tcx.features().staged_api()910                        && visitor.tcx.local_visibility(def_id).is_public()911                    {912                        if let hir::UseKind::Single(_) = tree.kind {913                            visitor.check_single_unstable_reexport(hir_id, tree.prefix);914                        } else {915                            visitor.check_glob_unstable_reexport(hir_id, def_id, tree.prefix);916                        }917                    }918                    // A use item can import something from two namespaces at the same time.919                    // For deprecation/stability we don't want to warn twice.920                    // This specifically happens with constructors for unit/tuple structs.921                    if let Some(res) = res.value_ns.or(res.type_ns) {922                        visitor.check_path(&Path { segments, res, span }, hir_id, stack);923                    }924925                    // Though, visit the macro namespace if it exists,926                    // regardless of the checks above relating to constructors.927                    if let Some(res) = res.macro_ns {928                        visitor.check_path(&Path { segments, res, span }, hir_id, stack);929                    }930                }931                hir::UseKind::Nested { items } => {932                    stack.push(tree.prefix.segments);933                    if items.is_empty() {934                        // need to handle `use foo::bar::{};`935                        visitor.check_path(936                            &Path {937                                segments,938                                res: segments.last().map_or(Res::Err, |seg| seg.res),939                                span,940                            },941                            hir_id,942                            stack,943                        );944                    } else {945                        for (tree, id, def_id) in items {946                            recurse(visitor, tree, *id, *def_id, stack);947                        }948                    }949                    stack.pop();950                }951            }952        }953        recurse(self, tree, hir_id, def_id, &mut v);954    }955956    fn visit_path(&mut self, path: &hir::Path<'tcx>, id: hir::HirId) {957        self.check_path(path, id, &[]);958959        intravisit::walk_path(self, path)960    }961}962963impl<'tcx> Checker<'tcx> {964    fn check_path(965        &mut self,966        path: &hir::Path<'tcx>,967        id: hir::HirId,968        prefix: &[&[hir::PathSegment<'tcx>]],969    ) {970        if let Some(def_id) = path.res.opt_def_id() {971            let method_span = path.segments.last().map(|s| s.ident.span);972            let item_is_allowed = self.tcx.check_stability_allow_unstable(973                def_id,974                Some(id),975                path.span,976                method_span,977                if is_unstable_reexport(self.tcx, id) {978                    AllowUnstable::Yes979                } else {980                    AllowUnstable::No981                },982            );983984            if item_is_allowed {985                // The item itself is allowed; check whether the path there is also allowed.986                let is_allowed_through_unstable_modules: Option<(Symbol, Symbol)> =987                    self.tcx.lookup_stability(def_id).and_then(|stab| match stab.level {988                        StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {989                            allowed_through_unstable_modules990                        }991                        _ => None,992                    });993                let segments = prefix994                    .into_iter()995                    .flat_map(|i| i.into_iter())996                    .chain(path.segments.iter().rev().skip(1).rev());997                let intrinsics_module = segments.clone().last();998                for segment in segments {999                    self.check_path_segments(1000                        path.span,1001                        segment,1002                        intrinsics_module,1003                        id,1004                        method_span,1005                        is_allowed_through_unstable_modules,1006                    );1007                }1008            }1009        }1010    }1011    /// Check parent modules stability as well if the item the path refers to is itself1012    /// stable. We only emit errors for unstable path segments if the item is stable1013    /// or allowed because stability is often inherited, so the most common case is that1014    /// both the segments and the item are unstable behind the same feature flag.1015    ///1016    /// We check here rather than in `visit_path_segment` to prevent visiting the last1017    /// path segment twice1018    ///1019    /// We include special cases via `#[rustc_allowed_through_unstable_modules]` for items1020    /// that were accidentally stabilized through unstable paths before this check was1021    /// added, such as `core::intrinsics::transmute`1022    fn check_path_segments(1023        &mut self,1024        span: Span,1025        path_segment: &hir::PathSegment<'_>,1026        intrinsics_module: Option<&hir::PathSegment<'_>>,1027        id: HirId,1028        method_span: Option<Span>,1029        is_allowed_through_unstable_modules: Option<(Symbol, Symbol)>,1030    ) {1031        // The item itself is allowed; check whether the path there is also allowed.10321033        if let Some(def_id) = path_segment.res.opt_def_id() {1034            match is_allowed_through_unstable_modules {1035                None => {1036                    // Emit a hard stability error if this path is not stable.10371038                    // use `None` for id to prevent deprecation check1039                    self.tcx.check_stability_allow_unstable(1040                        def_id,1041                        None,1042                        path_segment.ident.span,1043                        None,1044                        if is_unstable_reexport(self.tcx, id) {1045                            AllowUnstable::Yes1046                        } else {1047                            AllowUnstable::No1048                        },1049                    );1050                }1051                Some((message, suggestion)) => {1052                    // Call the stability check directly so that we can control which1053                    // diagnostic is emitted.1054                    let eval_result = self.tcx.eval_stability_allow_unstable(1055                        def_id,1056                        None,1057                        span,1058                        None,1059                        if is_unstable_reexport(self.tcx, id) {1060                            AllowUnstable::Yes1061                        } else {1062                            AllowUnstable::No1063                        },1064                    );1065                    let is_allowed = matches!(eval_result, EvalResult::Allow);1066                    if !is_allowed {1067                        // Show a deprecation message.1068                        let intrinsics_module = intrinsics_module.unwrap_or_else(|| {1069                            span_bug!(1070                                span,1071                                "no module for `is_allowed_through_unstable_modules` intrinsic {path_segment:?}"1072                            )1073                        });1074                        let diag = diagnostics::RustcAtumSuggestion {1075                            message,1076                            import_span: span,1077                            unstable_mod_span: { intrinsics_module.ident.span },1078                            module: intrinsics_module.ident,1079                            suggestion,1080                        };1081                        self.tcx.emit_node_span_lint(1082                            UNSTABLE_IMPORTS,1083                            id,1084                            method_span.unwrap_or(span),1085                            diag,1086                        );1087                    }1088                }1089            }1090        }1091    }1092}10931094/// Check whether a path is a `use` item that has been marked as unstable.1095///1096/// See issue #94972 for details on why this is a special case1097fn is_unstable_reexport(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {1098    // Get the LocalDefId so we can lookup the item to check the kind.1099    let def_id = id.owner.def_id;11001101    let Some(stab) = tcx.lookup_stability(def_id) else {1102        return false;1103    };11041105    if stab.level.is_stable() {1106        // The re-export is not marked as unstable, don't override1107        return false;1108    }11091110    // If this is a path that isn't a use, we don't need to do anything special1111    if !matches!(1112        tcx.hir_node(id),1113        hir::Node::Item(hir::Item { kind: ItemKind::Use(..), .. }) | hir::Node::NestedUseTree(_)1114    ) {1115        return false;1116    }11171118    true1119}11201121struct CheckTraitImplStable<'tcx> {1122    tcx: TyCtxt<'tcx>,1123    fully_stable: bool,1124}11251126impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> {1127    fn visit_path(&mut self, path: &hir::Path<'tcx>, _id: hir::HirId) {1128        if let Some(def_id) = path.res.opt_def_id()1129            && let Some(stab) = self.tcx.lookup_stability(def_id)1130        {1131            self.fully_stable &= stab.level.is_stable();1132        }1133        intravisit::walk_path(self, path)1134    }11351136    fn visit_trait_ref(&mut self, t: &'tcx TraitRef<'tcx>) {1137        if let Res::Def(DefKind::Trait, trait_did) = t.path.res {1138            if let Some(stab) = self.tcx.lookup_stability(trait_did) {1139                self.fully_stable &= stab.level.is_stable();1140            }1141        }1142        intravisit::walk_trait_ref(self, t)1143    }11441145    fn visit_ty(&mut self, t: &'tcx Ty<'tcx, AmbigArg>) {1146        if let TyKind::FnPtr(function) = t.kind {1147            if extern_abi_stability(function.abi).is_err() {1148                self.fully_stable = false;1149            }1150        }1151        intravisit::walk_ty(self, t)1152    }1153}11541155/// Given the list of enabled features that were not language features (i.e., that1156/// were expected to be library features), and the list of features used from1157/// libraries, identify activated features that don't exist and error about them.1158// This is `pub` for rustdoc. rustc should call it through `check_mod_unstable_api_usage`.1159pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {1160    let _prof_timer = tcx.sess.timer("unused_lib_feature_checking");11611162    let enabled_lang_features = tcx.features().enabled_lang_features();1163    let mut lang_features = UnordSet::default();1164    for EnabledLangFeature { gate_name, attr_sp, stable_since } in enabled_lang_features {1165        if let Some(version) = stable_since {1166            // Mark the feature as enabled, to ensure that it is not marked as unused.1167            let _ = tcx.features().enabled(*gate_name);11681169            // Warn if the user has enabled an already-stable lang feature.1170            unnecessary_stable_feature_lint(tcx, *attr_sp, *gate_name, *version);1171        }1172        if !lang_features.insert(gate_name) {1173            // Warn if the user enables a lang feature multiple times.1174            duplicate_feature_lint(tcx, *attr_sp, *gate_name);1175        }1176    }11771178    let enabled_lib_features = tcx.features().enabled_lib_features();1179    let mut remaining_lib_features = FxIndexMap::default();1180    for EnabledLibFeature { gate_name, attr_sp } in enabled_lib_features {1181        if remaining_lib_features.contains_key(gate_name) {1182            // Warn if the user enables a lib feature multiple times.1183            duplicate_feature_lint(tcx, *attr_sp, *gate_name);1184        }1185        remaining_lib_features.insert(*gate_name, *attr_sp);1186    }1187    // `stdbuild` has special handling for `libc`, so we need to1188    // recognise the feature when building std.1189    // Likewise, libtest is handled specially, so `test` isn't1190    // available as we'd like it to be.1191    // FIXME: only remove `libc` when `stdbuild` is enabled.1192    // FIXME: remove special casing for `test`.1193    // FIXME(#120456) - is `swap_remove` correct?1194    remaining_lib_features.swap_remove(&sym::libc);1195    remaining_lib_features.swap_remove(&sym::test);11961197    /// For each feature in `defined_features`..1198    ///1199    /// - If it is in `remaining_lib_features` (those features with `#![feature(..)]` attributes in1200    ///   the current crate), check if it is stable (or partially stable) and thus an unnecessary1201    ///   attribute.1202    /// - If it is in `remaining_implications` (a feature that is referenced by an `implied_by`1203    ///   from the current crate), then remove it from the remaining implications.1204    ///1205    /// Once this function has been invoked for every feature (local crate and all extern crates),1206    /// then..1207    ///1208    /// - If features remain in `remaining_lib_features`, then the user has enabled a feature that1209    ///   does not exist.1210    /// - If features remain in `remaining_implications`, the `implied_by` refers to a feature that1211    ///   does not exist.1212    ///1213    /// By structuring the code in this way: checking the features defined from each crate one at a1214    /// time, less loading from metadata is performed and thus compiler performance is improved.1215    fn check_features<'tcx>(1216        tcx: TyCtxt<'tcx>,1217        remaining_lib_features: &mut FxIndexMap<Symbol, Span>,1218        remaining_implications: &mut UnordMap<Symbol, Symbol>,1219        defined_features: &LibFeatures,1220        all_implications: &UnordMap<Symbol, Symbol>,1221    ) {1222        for (feature, stability) in defined_features.to_sorted_vec() {1223            if let FeatureStability::AcceptedSince(since) = stability1224                && let Some(span) = remaining_lib_features.get(&feature)1225            {1226                // Mark the feature as enabled, to ensure that it is not marked as unused.1227                let _ = tcx.features().enabled(feature);12281229                // Warn if the user has enabled an already-stable lib feature.1230                if let Some(implies) = all_implications.get(&feature) {1231                    unnecessary_partially_stable_feature_lint(tcx, *span, feature, *implies, since);1232                } else {1233                    unnecessary_stable_feature_lint(tcx, *span, feature, since);1234                }1235            }1236            // FIXME(#120456) - is `swap_remove` correct?1237            remaining_lib_features.swap_remove(&feature);12381239            // `feature` is the feature doing the implying, but `implied_by` is the feature with1240            // the attribute that establishes this relationship. `implied_by` is guaranteed to be a1241            // feature defined in the local crate because `remaining_implications` is only the1242            // implications from this crate.1243            remaining_implications.remove(&feature);12441245            if let FeatureStability::Unstable { old_name: Some(alias) } = stability1246                && let Some(span) = remaining_lib_features.swap_remove(&alias)1247            {1248                tcx.dcx().emit_err(diagnostics::RenamedFeature { span, feature, alias });1249            }12501251            if remaining_lib_features.is_empty() && remaining_implications.is_empty() {1252                break;1253            }1254        }1255    }12561257    // All local crate implications need to have the feature that implies it confirmed to exist.1258    let mut remaining_implications = tcx.stability_implications(LOCAL_CRATE).clone();12591260    // We always collect the lib features enabled in the current crate, even if there are1261    // no unknown features, because the collection also does feature attribute validation.1262    let local_defined_features = tcx.lib_features(LOCAL_CRATE);1263    if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {1264        let crates = tcx.crates(());12651266        // Loading the implications of all crates is unavoidable to be able to emit the partial1267        // stabilization diagnostic, but it can be avoided when there are no1268        // `remaining_lib_features`.1269        let mut all_implications = remaining_implications.clone();1270        for &cnum in crates {1271            all_implications1272                .extend_unord(tcx.stability_implications(cnum).items().map(|(k, v)| (*k, *v)));1273        }12741275        check_features(1276            tcx,1277            &mut remaining_lib_features,1278            &mut remaining_implications,1279            local_defined_features,1280            &all_implications,1281        );12821283        for &cnum in crates {1284            if remaining_lib_features.is_empty() && remaining_implications.is_empty() {1285                break;1286            }1287            check_features(1288                tcx,1289                &mut remaining_lib_features,1290                &mut remaining_implications,1291                tcx.lib_features(cnum),1292                &all_implications,1293            );1294        }12951296        if !remaining_lib_features.is_empty() {1297            let lang_features =1298                UNSTABLE_LANG_FEATURES.iter().map(|feature| feature.name).collect::<Vec<_>>();1299            let lib_features = crates1300                .iter()1301                .flat_map(|&cnum| {1302                    tcx.lib_features(cnum).stability.keys().copied().into_sorted_stable_ord()1303                })1304                .collect::<Vec<_>>();13051306            let valid_feature_names = [lang_features, lib_features].concat();13071308            // Collect all of the marked as "removed" features1309            let unstable_removed_features = crates1310                .iter()1311                .flat_map(|&cnum| {1312                    find_attr!(tcx, cnum.as_def_id(), UnstableRemoved(rem_features) => rem_features)1313                        .into_flat_iter()1314                })1315                .collect::<Vec<_>>();13161317            for (feature, span) in remaining_lib_features {1318                if let Some(removed) =1319                    unstable_removed_features.iter().find(|removed| removed.feature == feature)1320                {1321                    tcx.dcx().emit_err(diagnostics::FeatureRemoved {1322                        span,1323                        feature,1324                        reason: removed.reason,1325                        link: removed.link,1326                        since: removed.since.to_string(),1327                    });1328                } else {1329                    let suggestion =1330                        feature.find_similar(&valid_feature_names).map(|(actual_name, _)| {1331                            diagnostics::MisspelledFeature { span, actual_name }1332                        });1333                    tcx.dcx().emit_err(diagnostics::UnknownFeature { span, feature, suggestion });1334                }1335            }1336        }1337    }13381339    for (&implied_by, &feature) in remaining_implications.to_sorted_stable_ord() {1340        let local_defined_features = tcx.lib_features(LOCAL_CRATE);1341        let span = local_defined_features1342            .stability1343            .get(&feature)1344            .expect("feature that implied another does not exist")1345            .1;1346        tcx.dcx().emit_err(diagnostics::ImpliedFeatureNotExist { span, feature, implied_by });1347    }1348}13491350fn unnecessary_partially_stable_feature_lint(1351    tcx: TyCtxt<'_>,1352    span: Span,1353    feature: Symbol,1354    implies: Symbol,1355    since: Symbol,1356) {1357    tcx.emit_node_span_lint(1358        STABLE_FEATURES,1359        hir::CRATE_HIR_ID,1360        span,1361        diagnostics::UnnecessaryPartialStableFeature {1362            span,1363            line: tcx.sess.source_map().span_extend_to_line(span),1364            feature,1365            since,1366            implies,1367        },1368    );1369}13701371fn unnecessary_stable_feature_lint(1372    tcx: TyCtxt<'_>,1373    span: Span,1374    feature: Symbol,1375    mut since: Symbol,1376) {1377    if since.as_str() == VERSION_PLACEHOLDER {1378        since = sym::env_CFG_RELEASE;1379    }1380    tcx.emit_node_span_lint(1381        STABLE_FEATURES,1382        hir::CRATE_HIR_ID,1383        span,1384        diagnostics::UnnecessaryStableFeature { feature, since },1385    );1386}13871388fn duplicate_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol) {1389    tcx.emit_node_span_lint(1390        DUPLICATE_FEATURES,1391        hir::CRATE_HIR_ID,1392        span,1393        diagnostics::DuplicateFeature { feature },1394    );1395}

Code quality findings 14

Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let trait_span = self.tcx.def_ident_span(trait_id).unwrap();
Warning: Ignoring a Result or Option using 'let _ =' can hide errors or unexpected None values. Ensure the value is handled appropriately (match, if let, ?, expect) unless intentionally discarded with justification.
warning correctness discarded-result
let _ = tcx.features().enabled(*gate_name);
Warning: Ignoring a Result or Option using 'let _ =' can hide errors or unexpected None values. Ensure the value is handled appropriately (match, if let, ?, expect) unless intentionally discarded with justification.
warning correctness discarded-result
let _ = tcx.features().enabled(feature);
Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning correctness expect-usage
.expect("feature that implied another does not exist")
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match def_kind {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match def_kind {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match tcx.def_kind(tcx.local_parent(def_id)) {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &tcx.hir_node_by_def_id(def_id).expect_generic_param().kind {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match tcx.def_kind(tcx.local_parent(def_id)) {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match def_kind {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
self.tcx.lookup_stability(def_id).and_then(|stab| match stab.level {
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
let intrinsics_module = segments.clone().last();
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
let mut all_implications = remaining_implications.clone();
Performance Info: Calling .to_string() (especially on &str) allocates a new String. If done repeatedly in loops, consider alternatives like working with &str or using crates like `itoa`/`ryu` for number-to-string conversion.
info performance to-string-in-loop
since: removed.since.to_string(),

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.

Generated by searchcode.com · powered by code_get_file + code_get_findings