compiler/rustc_metadata/src/creader.rs RUST 1,522 lines View on github.com → Search inside
1//! Validates all used crates and extern libraries and loads their metadata23use std::collections::BTreeMap;4use std::error::Error;5use std::path::Path;6use std::str::FromStr;7use std::time::Duration;8use std::{cmp, env, iter};910use rustc_ast::expand::allocator::{ALLOC_ERROR_HANDLER, AllocatorKind, global_fn_name};11use rustc_ast::{self as ast, *};12use rustc_data_structures::fx::FxHashSet;13use rustc_data_structures::owned_slice::OwnedSlice;14use rustc_data_structures::svh::Svh;15use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard};16use rustc_data_structures::unord::UnordMap;17use rustc_expand::base::SyntaxExtension;18use rustc_fs_util::try_canonicalize;19use rustc_hir as hir;20use rustc_hir::def_id::{CrateNum, LOCAL_CRATE, LocalDefId, StableCrateId};21use rustc_hir::definitions::Definitions;22use rustc_index::IndexVec;23use rustc_middle::bug;24use rustc_middle::ty::data_structures::IndexSet;25use rustc_middle::ty::{TyCtxt, TyCtxtFeed};26use rustc_proc_macro::bridge::client::ProcMacro;27use rustc_session::config::mitigation_coverage::DeniedPartialMitigationLevel;28use rustc_session::config::{29    CrateType, ExtendedTargetModifierInfo, ExternLocation, Externs, OptionsTargetModifiers,30    TargetModifier,31};32use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateSource};33use rustc_session::output::validate_crate_name;34use rustc_session::search_paths::PathKind;35use rustc_session::{Session, lint};36use rustc_span::def_id::DefId;37use rustc_span::edition::Edition;38use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};39use rustc_target::spec::{PanicStrategy, Target};40use tracing::{debug, info, trace};4142use crate::errors;43use crate::locator::{CrateError, CrateLocator, CratePaths, CrateRejections};44use crate::rmeta::{45    CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob, TargetModifiers,46};4748/// The backend's way to give the crate store access to the metadata in a library.49/// Note that it returns the raw metadata bytes stored in the library file, whether50/// it is compressed, uncompressed, some weird mix, etc.51/// rmeta files are backend independent and not handled here.52pub trait MetadataLoader {53    fn get_rlib_metadata(&self, target: &Target, filename: &Path) -> Result<OwnedSlice, String>;54    fn get_dylib_metadata(&self, target: &Target, filename: &Path) -> Result<OwnedSlice, String>;55}5657pub type MetadataLoaderDyn = dyn MetadataLoader + Send + Sync + sync::DynSend + sync::DynSync;5859pub struct CStore {60    metadata_loader: Box<MetadataLoaderDyn>,6162    metas: IndexVec<CrateNum, Option<Box<CrateMetadata>>>,63    injected_panic_runtime: Option<CrateNum>,64    /// This crate needs an allocator and either provides it itself, or finds it in a dependency.65    /// If the above is true, then this field denotes the kind of the found allocator.66    allocator_kind: Option<AllocatorKind>,67    /// This crate needs an allocation error handler and either provides it itself, or finds it in a dependency.68    /// If the above is true, then this field denotes the kind of the found allocator.69    alloc_error_handler_kind: Option<AllocatorKind>,70    /// This crate has a `#[global_allocator]` item.71    has_global_allocator: bool,72    /// This crate has a `#[alloc_error_handler]` item.73    has_alloc_error_handler: bool,7475    /// Names that were used to load the crates via `extern crate` or paths.76    resolved_externs: UnordMap<Symbol, CrateNum>,7778    /// Unused externs of the crate79    unused_externs: Vec<Symbol>,8081    used_extern_options: FxHashSet<Symbol>,82    /// Whether there was a failure in resolving crate,83    /// it's used to suppress some diagnostics that would otherwise too noisey.84    has_crate_resolve_with_fail: bool,85}8687impl std::fmt::Debug for CStore {88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {89        f.debug_struct("CStore").finish_non_exhaustive()90    }91}9293pub enum LoadedMacro {94    MacroDef {95        def: MacroDef,96        ident: Ident,97        attrs: Vec<hir::Attribute>,98        span: Span,99        edition: Edition,100    },101    ProcMacro(SyntaxExtension),102}103104pub(crate) struct Library {105    pub source: CrateSource,106    pub metadata: MetadataBlob,107}108109enum LoadResult {110    Previous(CrateNum),111    Loaded(Library),112}113114struct CrateDump<'a>(&'a CStore);115116impl<'a> std::fmt::Debug for CrateDump<'a> {117    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {118        writeln!(fmt, "resolved crates:")?;119        for (cnum, data) in self.0.iter_crate_data() {120            writeln!(fmt, "  name: {}", data.name())?;121            writeln!(fmt, "  cnum: {cnum}")?;122            writeln!(fmt, "  hash: {}", data.hash())?;123            writeln!(fmt, "  reqd: {:?}", data.dep_kind())?;124            writeln!(fmt, "  priv: {:?}", data.is_private_dep())?;125            let CrateSource { dylib, rlib, rmeta, sdylib_interface } = data.source();126            if let Some(dylib) = dylib {127                writeln!(fmt, "  dylib: {}", dylib.display())?;128            }129            if let Some(rlib) = rlib {130                writeln!(fmt, "   rlib: {}", rlib.display())?;131            }132            if let Some(rmeta) = rmeta {133                writeln!(fmt, "   rmeta: {}", rmeta.display())?;134            }135            if let Some(sdylib_interface) = sdylib_interface {136                writeln!(fmt, "   sdylib interface: {}", sdylib_interface.display())?;137            }138        }139        Ok(())140    }141}142143/// Reason that a crate is being sourced as a dependency.144#[derive(Clone, Copy)]145enum CrateOrigin<'a> {146    /// This crate was a dependency of another crate.147    IndirectDependency {148        /// Where this dependency was included from. Should only be used in error messages.149        dep_root_for_errors: &'a CratePaths,150        /// True if the parent is private, meaning the dependent should also be private.151        parent_private: bool,152        /// Dependency info about this crate.153        dep: &'a CrateDep,154    },155    /// Injected by `rustc`.156    Injected,157    /// Provided by `extern crate foo` or as part of the extern prelude.158    Extern,159}160161impl<'a> CrateOrigin<'a> {162    /// Return the dependency root, if any.163    fn dep_root_for_errors(&self) -> Option<&'a CratePaths> {164        match self {165            CrateOrigin::IndirectDependency { dep_root_for_errors, .. } => {166                Some(dep_root_for_errors)167            }168            _ => None,169        }170    }171172    /// Return dependency information, if any.173    fn dep(&self) -> Option<&'a CrateDep> {174        match self {175            CrateOrigin::IndirectDependency { dep, .. } => Some(dep),176            _ => None,177        }178    }179180    /// `Some(true)` if the dependency is private or its parent is private, `Some(false)` if the181    /// dependency is not private, `None` if it could not be determined.182    fn private_dep(&self) -> Option<bool> {183        match self {184            CrateOrigin::IndirectDependency { parent_private, dep, .. } => {185                Some(dep.is_private || *parent_private)186            }187            CrateOrigin::Injected => Some(true),188            _ => None,189        }190    }191}192193impl CStore {194    pub fn from_tcx(tcx: TyCtxt<'_>) -> FreezeReadGuard<'_, CStore> {195        FreezeReadGuard::map(tcx.untracked().cstore.read(), |cstore| {196            cstore.as_any().downcast_ref::<CStore>().expect("`tcx.cstore` is not a `CStore`")197        })198    }199200    pub fn from_tcx_mut(tcx: TyCtxt<'_>) -> FreezeWriteGuard<'_, CStore> {201        FreezeWriteGuard::map(tcx.untracked().cstore.write(), |cstore| {202            cstore.untracked_as_any().downcast_mut().expect("`tcx.cstore` is not a `CStore`")203        })204    }205206    fn intern_stable_crate_id<'tcx>(207        &mut self,208        tcx: TyCtxt<'tcx>,209        root: &CrateRoot,210    ) -> Result<TyCtxtFeed<'tcx, CrateNum>, CrateError> {211        assert_eq!(self.metas.len(), tcx.untracked().stable_crate_ids.read().len());212        let num = tcx.create_crate_num(root.stable_crate_id()).map_err(|existing| {213            // Check for (potential) conflicts with the local crate214            if existing == LOCAL_CRATE {215                CrateError::SymbolConflictsCurrent(root.name())216            } else if let Some(crate_name1) = self.metas[existing].as_ref().map(|data| data.name())217            {218                let crate_name0 = root.name();219                CrateError::StableCrateIdCollision(crate_name0, crate_name1)220            } else {221                CrateError::NotFound(root.name())222            }223        })?;224225        self.metas.push(None);226        Ok(num)227    }228229    pub fn has_crate_data(&self, cnum: CrateNum) -> bool {230        self.metas[cnum].is_some()231    }232233    pub(crate) fn get_crate_data(&self, cnum: CrateNum) -> &CrateMetadata {234        self.metas[cnum].as_ref().unwrap_or_else(|| panic!("Failed to get crate data for {cnum:?}"))235    }236237    pub(crate) fn get_crate_data_mut(&mut self, cnum: CrateNum) -> &mut CrateMetadata {238        self.metas[cnum].as_mut().unwrap_or_else(|| panic!("Failed to get crate data for {cnum:?}"))239    }240241    fn set_crate_data(&mut self, cnum: CrateNum, data: CrateMetadata) {242        assert!(self.metas[cnum].is_none(), "Overwriting crate metadata entry");243        self.metas[cnum] = Some(Box::new(data));244    }245246    /// Save the name used to resolve the extern crate in the local crate247    ///248    /// The name isn't always the crate's own name, because `sess.opts.externs` can assign it another name.249    /// It's also not always the same as the `DefId`'s symbol due to renames `extern crate resolved_name as defid_name`.250    pub(crate) fn set_resolved_extern_crate_name(&mut self, name: Symbol, extern_crate: CrateNum) {251        self.resolved_externs.insert(name, extern_crate);252    }253254    /// Crate resolved and loaded via the given extern name255    /// (corresponds to names in `sess.opts.externs`)256    ///257    /// May be `None` if the crate wasn't used258    pub fn resolved_extern_crate(&self, externs_name: Symbol) -> Option<CrateNum> {259        self.resolved_externs.get(&externs_name).copied()260    }261262    pub(crate) fn iter_crate_data(&self) -> impl Iterator<Item = (CrateNum, &CrateMetadata)> {263        self.metas264            .iter_enumerated()265            .filter_map(|(cnum, data)| data.as_deref().map(|data| (cnum, data)))266    }267268    pub fn all_proc_macro_def_ids(&self, tcx: TyCtxt<'_>) -> impl Iterator<Item = DefId> {269        self.iter_crate_data().flat_map(move |(krate, data)| data.proc_macros_for_crate(tcx, krate))270    }271272    fn push_dependencies_in_postorder(&self, deps: &mut IndexSet<CrateNum>, cnum: CrateNum) {273        if !deps.contains(&cnum) {274            let cdata = self.get_crate_data(cnum);275            for dep in cdata.dependencies() {276                if dep != cnum {277                    self.push_dependencies_in_postorder(deps, dep);278                }279            }280281            deps.insert(cnum);282        }283    }284285    pub(crate) fn crate_dependencies_in_postorder(&self, cnum: CrateNum) -> IndexSet<CrateNum> {286        let mut deps = IndexSet::default();287        if cnum == LOCAL_CRATE {288            for (cnum, _) in self.iter_crate_data() {289                self.push_dependencies_in_postorder(&mut deps, cnum);290            }291        } else {292            self.push_dependencies_in_postorder(&mut deps, cnum);293        }294        deps295    }296297    pub(crate) fn injected_panic_runtime(&self) -> Option<CrateNum> {298        self.injected_panic_runtime299    }300301    pub(crate) fn allocator_kind(&self) -> Option<AllocatorKind> {302        self.allocator_kind303    }304305    pub(crate) fn alloc_error_handler_kind(&self) -> Option<AllocatorKind> {306        self.alloc_error_handler_kind307    }308309    pub(crate) fn has_global_allocator(&self) -> bool {310        self.has_global_allocator311    }312313    pub(crate) fn has_alloc_error_handler(&self) -> bool {314        self.has_alloc_error_handler315    }316317    pub fn had_extern_crate_load_failure(&self) -> bool {318        self.has_crate_resolve_with_fail319    }320321    pub fn report_unused_deps(&self, tcx: TyCtxt<'_>) {322        let json_unused_externs = tcx.sess.opts.json_unused_externs;323324        // We put the check for the option before the lint_level_at_node call325        // because the call mutates internal state and introducing it326        // leads to some ui tests failing.327        if !json_unused_externs.is_enabled() {328            return;329        }330        let level = tcx331            .lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID)332            .level;333        if level != lint::Level::Allow {334            let unused_externs =335                self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>();336            let unused_externs = unused_externs.iter().map(String::as_str).collect::<Vec<&str>>();337            tcx.dcx().emit_unused_externs(level, json_unused_externs.is_loud(), &unused_externs);338        }339    }340341    fn report_target_modifiers_extended(342        tcx: TyCtxt<'_>,343        krate: &Crate,344        mods: &TargetModifiers,345        dep_mods: &TargetModifiers,346        data: &CrateMetadata,347    ) {348        let span = krate.spans.inner_span.shrink_to_lo();349        let allowed_flag_mismatches = &tcx.sess.opts.cg.unsafe_allow_abi_mismatch;350        let local_crate = tcx.crate_name(LOCAL_CRATE);351        let tmod_extender = |tmod: &TargetModifier| (tmod.extend(), tmod.clone());352        let report_diff = |prefix: &String,353                           opt_name: &String,354                           flag_local_value: Option<&String>,355                           flag_extern_value: Option<&String>| {356            if allowed_flag_mismatches.contains(&opt_name) {357                return;358            }359            let extern_crate = data.name();360            let flag_name = opt_name.clone();361            let flag_name_prefixed = format!("-{}{}", prefix, opt_name);362363            match (flag_local_value, flag_extern_value) {364                (Some(local_value), Some(extern_value)) => {365                    tcx.dcx().emit_err(errors::IncompatibleTargetModifiers {366                        span,367                        extern_crate,368                        local_crate,369                        flag_name,370                        flag_name_prefixed,371                        local_value: local_value.to_string(),372                        extern_value: extern_value.to_string(),373                    })374                }375                (None, Some(extern_value)) => {376                    tcx.dcx().emit_err(errors::IncompatibleTargetModifiersLMissed {377                        span,378                        extern_crate,379                        local_crate,380                        flag_name,381                        flag_name_prefixed,382                        extern_value: extern_value.to_string(),383                    })384                }385                (Some(local_value), None) => {386                    tcx.dcx().emit_err(errors::IncompatibleTargetModifiersRMissed {387                        span,388                        extern_crate,389                        local_crate,390                        flag_name,391                        flag_name_prefixed,392                        local_value: local_value.to_string(),393                    })394                }395                (None, None) => panic!("Incorrect target modifiers report_diff(None, None)"),396            };397        };398        let mut it1 = mods.iter().map(tmod_extender);399        let mut it2 = dep_mods.iter().map(tmod_extender);400        let mut left_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None;401        let mut right_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None;402        loop {403            left_name_val = left_name_val.or_else(|| it1.next());404            right_name_val = right_name_val.or_else(|| it2.next());405            match (&left_name_val, &right_name_val) {406                (Some(l), Some(r)) => match l.1.opt.cmp(&r.1.opt) {407                    cmp::Ordering::Equal => {408                        if !l.1.consistent(&tcx.sess, Some(&r.1)) {409                            report_diff(410                                &l.0.prefix,411                                &l.0.name,412                                Some(&l.1.value_name),413                                Some(&r.1.value_name),414                            );415                        }416                        left_name_val = None;417                        right_name_val = None;418                    }419                    cmp::Ordering::Greater => {420                        if !r.1.consistent(&tcx.sess, None) {421                            report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));422                        }423                        right_name_val = None;424                    }425                    cmp::Ordering::Less => {426                        if !l.1.consistent(&tcx.sess, None) {427                            report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);428                        }429                        left_name_val = None;430                    }431                },432                (Some(l), None) => {433                    if !l.1.consistent(&tcx.sess, None) {434                        report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);435                    }436                    left_name_val = None;437                }438                (None, Some(r)) => {439                    if !r.1.consistent(&tcx.sess, None) {440                        report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));441                    }442                    right_name_val = None;443                }444                (None, None) => break,445            }446        }447    }448449    pub fn report_session_incompatibilities(&self, tcx: TyCtxt<'_>, krate: &Crate) {450        self.report_incompatible_target_modifiers(tcx, krate);451        self.report_incompatible_partial_mitigations(tcx, krate);452        self.report_incompatible_async_drop_feature(tcx, krate);453    }454455    pub fn report_incompatible_target_modifiers(&self, tcx: TyCtxt<'_>, krate: &Crate) {456        for flag_name in &tcx.sess.opts.cg.unsafe_allow_abi_mismatch {457            if !OptionsTargetModifiers::is_target_modifier(flag_name) {458                tcx.dcx().emit_err(errors::UnknownTargetModifierUnsafeAllowed {459                    span: krate.spans.inner_span.shrink_to_lo(),460                    flag_name: flag_name.clone(),461                });462            }463        }464        let mods = tcx.sess.opts.gather_target_modifiers();465        for (_cnum, data) in self.iter_crate_data() {466            if data.is_proc_macro_crate() {467                continue;468            }469            let dep_mods = data.target_modifiers();470            if mods != dep_mods {471                Self::report_target_modifiers_extended(tcx, krate, &mods, &dep_mods, data);472            }473        }474    }475476    pub fn report_incompatible_partial_mitigations(&self, tcx: TyCtxt<'_>, krate: &Crate) {477        let my_mitigations = tcx.sess.gather_enabled_denied_partial_mitigations();478        let mut my_mitigations: BTreeMap<_, _> =479            my_mitigations.iter().map(|mitigation| (mitigation.kind, mitigation)).collect();480        for skipped_mitigation in tcx.sess.opts.allowed_partial_mitigations(tcx.sess.edition()) {481            my_mitigations.remove(&skipped_mitigation);482        }483        const MAX_ERRORS_PER_MITIGATION: usize = 5;484        let mut errors_per_mitigation = BTreeMap::new();485        for (_cnum, data) in self.iter_crate_data() {486            if data.is_proc_macro_crate() {487                continue;488            }489            let their_mitigations = data.enabled_denied_partial_mitigations();490            for my_mitigation in my_mitigations.values() {491                let their_mitigation = their_mitigations492                    .iter()493                    .find(|mitigation| mitigation.kind == my_mitigation.kind)494                    .map_or(DeniedPartialMitigationLevel::Enabled(false), |m| m.level);495                if their_mitigation < my_mitigation.level {496                    let errors = errors_per_mitigation.entry(my_mitigation.kind).or_insert(0);497                    if *errors >= MAX_ERRORS_PER_MITIGATION {498                        continue;499                    }500                    *errors += 1;501502                    tcx.dcx().emit_err(errors::MitigationLessStrictInDependency {503                        span: krate.spans.inner_span.shrink_to_lo(),504                        mitigation_name: my_mitigation.kind.to_string(),505                        mitigation_level: my_mitigation.level.level_str().to_string(),506                        extern_crate: data.name(),507                    });508                }509            }510        }511    }512513    // Report about async drop types in dependency if async drop feature is disabled514    pub fn report_incompatible_async_drop_feature(&self, tcx: TyCtxt<'_>, krate: &Crate) {515        if tcx.features().async_drop() {516            return;517        }518        for (_cnum, data) in self.iter_crate_data() {519            if data.is_proc_macro_crate() {520                continue;521            }522            if data.has_async_drops() {523                let extern_crate = data.name();524                let local_crate = tcx.crate_name(LOCAL_CRATE);525                tcx.dcx().emit_warn(errors::AsyncDropTypesInDependency {526                    span: krate.spans.inner_span.shrink_to_lo(),527                    extern_crate,528                    local_crate,529                });530            }531        }532    }533534    pub fn new(metadata_loader: Box<MetadataLoaderDyn>) -> CStore {535        CStore {536            metadata_loader,537            // We add an empty entry for LOCAL_CRATE (which maps to zero) in538            // order to make array indices in `metas` match with the539            // corresponding `CrateNum`. This first entry will always remain540            // `None`.541            metas: IndexVec::from_iter(iter::once(None)),542            injected_panic_runtime: None,543            allocator_kind: None,544            alloc_error_handler_kind: None,545            has_global_allocator: false,546            has_alloc_error_handler: false,547            resolved_externs: UnordMap::default(),548            unused_externs: Vec::new(),549            used_extern_options: Default::default(),550            has_crate_resolve_with_fail: false,551        }552    }553554    fn existing_match(&self, name: Symbol, hash: Option<Svh>) -> Option<CrateNum> {555        let hash = hash?;556557        for (cnum, data) in self.iter_crate_data() {558            if data.name() != name {559                trace!("{} did not match {}", data.name(), name);560                continue;561            }562563            if hash == data.hash() {564                return Some(cnum);565            } else {566                debug!("actual hash {} did not match expected {}", hash, data.hash());567            }568        }569570        None571    }572573    /// Determine whether a dependency should be considered private.574    ///575    /// Dependencies are private if they get extern option specified, e.g. `--extern priv:mycrate`.576    /// This is stored in metadata, so `private_dep`  can be correctly set during load. A `Some`577    /// value for `private_dep` indicates that the crate is known to be private or public (note578    /// that any `None` or `Some(false)` use of the same crate will make it public).579    ///580    /// Sometimes the directly dependent crate is not specified by `--extern`, in this case,581    /// `private-dep` is none during loading. This is equivalent to the scenario where the582    /// command parameter is set to `public-dependency`583    fn is_private_dep(&self, externs: &Externs, name: Symbol, private_dep: Option<bool>) -> bool {584        let extern_private = externs.get(name.as_str()).map(|e| e.is_private_dep);585        match (extern_private, private_dep) {586            // Explicit non-private via `--extern`, explicit non-private from metadata, or587            // unspecified with default to public.588            (Some(false), _) | (_, Some(false)) | (None, None) => false,589            // Marked private via `--extern priv:mycrate` or in metadata.590            (Some(true) | None, Some(true) | None) => true,591        }592    }593594    fn register_crate<'tcx>(595        &mut self,596        tcx: TyCtxt<'tcx>,597        host_lib: Option<Library>,598        origin: CrateOrigin<'_>,599        lib: Library,600        dep_kind: CrateDepKind,601        name: Symbol,602        private_dep: Option<bool>,603    ) -> Result<CrateNum, CrateError> {604        let _prof_timer =605            tcx.sess.prof.generic_activity_with_arg("metadata_register_crate", name.as_str());606607        let Library { source, metadata } = lib;608        let crate_root = metadata.get_root();609        let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash());610        let private_dep = self.is_private_dep(&tcx.sess.opts.externs, name, private_dep);611612        // Claim this crate number and cache it613        let feed = self.intern_stable_crate_id(tcx, &crate_root)?;614        let cnum = feed.key();615616        info!(617            "register crate `{}` (cnum = {}. private_dep = {})",618            crate_root.name(),619            cnum,620            private_dep621        );622623        // Maintain a reference to the top most crate.624        // Stash paths for top-most crate locally if necessary.625        let crate_paths;626        let dep_root_for_errors = if let Some(dep_root_for_errors) = origin.dep_root_for_errors() {627            dep_root_for_errors628        } else {629            crate_paths = CratePaths::new(crate_root.name(), source.clone());630            &crate_paths631        };632633        let cnum_map = self.resolve_crate_deps(634            tcx,635            dep_root_for_errors,636            &crate_root,637            &metadata,638            cnum,639            dep_kind,640            private_dep,641        )?;642643        let raw_proc_macros = if crate_root.is_proc_macro_crate() {644            let temp_root;645            let (dlsym_source, dlsym_root) = match &host_lib {646                Some(host_lib) => (&host_lib.source, {647                    temp_root = host_lib.metadata.get_root();648                    &temp_root649                }),650                None => (&source, &crate_root),651            };652            let dlsym_dylib = dlsym_source.dylib.as_ref().expect("no dylib for a proc-macro crate");653            Some(self.dlsym_proc_macros(tcx.sess, dlsym_dylib, dlsym_root.stable_crate_id())?)654        } else {655            None656        };657658        let crate_metadata = CrateMetadata::new(659            tcx,660            metadata,661            crate_root,662            raw_proc_macros,663            cnum,664            cnum_map,665            dep_kind,666            source,667            private_dep,668            host_hash,669        );670671        self.set_crate_data(cnum, crate_metadata);672673        Ok(cnum)674    }675676    fn load_proc_macro<'a, 'b>(677        &self,678        sess: &'a Session,679        locator: &mut CrateLocator<'b>,680        crate_rejections: &mut CrateRejections,681        path_kind: PathKind,682        host_hash: Option<Svh>,683    ) -> Result<Option<(LoadResult, Option<Library>)>, CrateError>684    where685        'a: 'b,686    {687        if sess.opts.unstable_opts.dual_proc_macros {688            // Use a new crate locator and crate rejections so trying to load a proc macro doesn't689            // affect the error message we emit690            let mut proc_macro_locator = locator.clone();691692            // Try to load a proc macro693            proc_macro_locator.for_target_proc_macro(sess, path_kind);694695            // Load the proc macro crate for the target696            let target_result =697                match self.load(&mut proc_macro_locator, &mut CrateRejections::default())? {698                    Some(LoadResult::Previous(cnum)) => {699                        return Ok(Some((LoadResult::Previous(cnum), None)));700                    }701                    Some(LoadResult::Loaded(library)) => Some(LoadResult::Loaded(library)),702                    None => return Ok(None),703                };704705            // Use the existing crate_rejections as we want the error message to be affected by706            // loading the host proc macro.707            *crate_rejections = CrateRejections::default();708709            // Load the proc macro crate for the host710            locator.for_proc_macro(sess, path_kind);711712            locator.hash = host_hash;713714            let Some(host_result) = self.load(locator, crate_rejections)? else {715                return Ok(None);716            };717718            let host_result = match host_result {719                LoadResult::Previous(..) => {720                    panic!("host and target proc macros must be loaded in lock-step")721                }722                LoadResult::Loaded(library) => library,723            };724            Ok(Some((target_result.unwrap(), Some(host_result))))725        } else {726            // Use a new crate locator and crate rejections so trying to load a proc macro doesn't727            // affect the error message we emit728            let mut proc_macro_locator = locator.clone();729730            // Load the proc macro crate for the host731            proc_macro_locator.for_proc_macro(sess, path_kind);732733            let Some(host_result) =734                self.load(&mut proc_macro_locator, &mut CrateRejections::default())?735            else {736                return Ok(None);737            };738739            Ok(Some((host_result, None)))740        }741    }742743    fn resolve_crate<'tcx>(744        &mut self,745        tcx: TyCtxt<'tcx>,746        name: Symbol,747        span: Span,748        dep_kind: CrateDepKind,749        origin: CrateOrigin<'_>,750    ) -> Option<CrateNum> {751        self.used_extern_options.insert(name);752        match self.maybe_resolve_crate(tcx, name, dep_kind, origin) {753            Ok(cnum) => {754                self.set_used_recursively(cnum);755                Some(cnum)756            }757            Err(err) => {758                debug!("failed to resolve crate {} {:?}", name, dep_kind);759                // crate maybe injrected with `standard_library_imports::inject`, their span is dummy.760                // we ignore compiler-injected prelude/sysroot loads here so they don't suppress761                // unrelated diagnostics, such as `unsupported targets for std library` etc,762                // these maybe helpful for users to resolve crate loading failure.763                if !tcx.sess.dcx().has_errors().is_some() && !span.is_dummy() {764                    self.has_crate_resolve_with_fail = true;765                }766                let missing_core = self767                    .maybe_resolve_crate(768                        tcx,769                        sym::core,770                        CrateDepKind::Unconditional,771                        CrateOrigin::Extern,772                    )773                    .is_err();774                err.report(tcx.sess, span, missing_core);775                None776            }777        }778    }779780    fn maybe_resolve_crate<'b, 'tcx>(781        &'b mut self,782        tcx: TyCtxt<'tcx>,783        name: Symbol,784        mut dep_kind: CrateDepKind,785        origin: CrateOrigin<'b>,786    ) -> Result<CrateNum, CrateError> {787        info!("resolving crate `{}`", name);788        if !name.as_str().is_ascii() {789            return Err(CrateError::NonAsciiName(name));790        }791792        let dep_root_for_errors = origin.dep_root_for_errors();793        let dep = origin.dep();794        let hash = dep.map(|d| d.hash);795        let host_hash = dep.map(|d| d.host_hash).flatten();796        let extra_filename = dep.map(|d| &d.extra_filename[..]);797        let path_kind = if dep.is_some() { PathKind::Dependency } else { PathKind::Crate };798        let private_dep = origin.private_dep();799800        let result = if let Some(cnum) = self.existing_match(name, hash) {801            (LoadResult::Previous(cnum), None)802        } else {803            info!("falling back to a load");804            let mut locator = CrateLocator::new(805                tcx.sess,806                &*self.metadata_loader,807                name,808                // The all loop is because `--crate-type=rlib --crate-type=rlib` is809                // legal and produces both inside this type.810                tcx.crate_types().iter().all(|c| *c == CrateType::Rlib),811                hash,812                extra_filename,813                path_kind,814            );815            let mut crate_rejections = CrateRejections::default();816817            match self.load(&mut locator, &mut crate_rejections)? {818                Some(res) => (res, None),819                None => {820                    info!("falling back to loading proc_macro");821                    dep_kind = CrateDepKind::MacrosOnly;822                    match self.load_proc_macro(823                        tcx.sess,824                        &mut locator,825                        &mut crate_rejections,826                        path_kind,827                        host_hash,828                    )? {829                        Some(res) => res,830                        None => {831                            return Err(832                                locator.into_error(crate_rejections, dep_root_for_errors.cloned())833                            );834                        }835                    }836                }837            }838        };839840        match result {841            (LoadResult::Previous(cnum), None) => {842                info!("library for `{}` was loaded previously, cnum {cnum}", name);843                // When `private_dep` is none, it indicates the directly dependent crate. If it is844                // not specified by `--extern` on command line parameters, it may be845                // `private-dependency` when `register_crate` is called for the first time. Then it must be updated to846                // `public-dependency` here.847                let private_dep = self.is_private_dep(&tcx.sess.opts.externs, name, private_dep);848                let cdata = self.get_crate_data_mut(cnum);849                if cdata.is_proc_macro_crate() {850                    dep_kind = CrateDepKind::MacrosOnly;851                }852                cdata.set_dep_kind(cmp::max(cdata.dep_kind(), dep_kind));853                cdata.update_and_private_dep(private_dep);854                Ok(cnum)855            }856            (LoadResult::Loaded(library), host_library) => {857                info!("register newly loaded library for `{}`", name);858                self.register_crate(tcx, host_library, origin, library, dep_kind, name, private_dep)859            }860            _ => panic!(),861        }862    }863864    fn load(865        &self,866        locator: &CrateLocator<'_>,867        crate_rejections: &mut CrateRejections,868    ) -> Result<Option<LoadResult>, CrateError> {869        let Some(library) = locator.maybe_load_library_crate(crate_rejections)? else {870            return Ok(None);871        };872873        // In the case that we're loading a crate, but not matching874        // against a hash, we could load a crate which has the same hash875        // as an already loaded crate. If this is the case prevent876        // duplicates by just using the first crate.877        let root = library.metadata.get_root();878        let mut result = LoadResult::Loaded(library);879        for (cnum, data) in self.iter_crate_data() {880            if data.name() == root.name() && root.hash() == data.hash() {881                assert!(locator.hash.is_none());882                info!("load success, going to previous cnum: {}", cnum);883                result = LoadResult::Previous(cnum);884                break;885            }886        }887        Ok(Some(result))888    }889890    /// Go through the crate metadata and load any crates that it references.891    fn resolve_crate_deps(892        &mut self,893        tcx: TyCtxt<'_>,894        dep_root_for_errors: &CratePaths,895        crate_root: &CrateRoot,896        metadata: &MetadataBlob,897        krate: CrateNum,898        dep_kind: CrateDepKind,899        parent_is_private: bool,900    ) -> Result<CrateNumMap, CrateError> {901        debug!(902            "resolving deps of external crate `{}` with dep root `{}`",903            crate_root.name(),904            dep_root_for_errors.name905        );906        if crate_root.is_proc_macro_crate() {907            return Ok(CrateNumMap::new());908        }909910        // The map from crate numbers in the crate we're resolving to local crate numbers.911        // We map 0 and all other holes in the map to our parent crate. The "additional"912        // self-dependencies should be harmless.913        let deps = crate_root.decode_crate_deps(metadata);914        let mut crate_num_map = CrateNumMap::with_capacity(1 + deps.len());915        crate_num_map.push(krate);916        for dep in deps {917            info!(918                "resolving dep `{}`->`{}` hash: `{}` extra filename: `{}` private {}",919                crate_root.name(),920                dep.name,921                dep.hash,922                dep.extra_filename,923                dep.is_private,924            );925            let dep_kind = match dep_kind {926                CrateDepKind::MacrosOnly => CrateDepKind::MacrosOnly,927                _ => dep.kind,928            };929            let cnum = self.maybe_resolve_crate(930                tcx,931                dep.name,932                dep_kind,933                CrateOrigin::IndirectDependency {934                    dep_root_for_errors,935                    parent_private: parent_is_private,936                    dep: &dep,937                },938            )?;939            crate_num_map.push(cnum);940        }941942        debug!("resolve_crate_deps: cnum_map for {:?} is {:?}", krate, crate_num_map);943        Ok(crate_num_map)944    }945946    fn dlsym_proc_macros(947        &self,948        sess: &Session,949        path: &Path,950        stable_crate_id: StableCrateId,951    ) -> Result<&'static [ProcMacro], CrateError> {952        let sym_name = sess.generate_proc_macro_decls_symbol(stable_crate_id);953        debug!("trying to dlsym proc_macros {} for symbol `{}`", path.display(), sym_name);954955        unsafe {956            let result = load_symbol_from_dylib::<*const &[ProcMacro]>(path, &sym_name);957            match result {958                Ok(result) => {959                    debug!("loaded dlsym proc_macros {} for symbol `{}`", path.display(), sym_name);960                    Ok(*result)961                }962                Err(err) => {963                    debug!(964                        "failed to dlsym proc_macros {} for symbol `{}`",965                        path.display(),966                        sym_name967                    );968                    Err(err.into())969                }970            }971        }972    }973974    fn inject_panic_runtime(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) {975        // If we're only compiling an rlib, then there's no need to select a976        // panic runtime, so we just skip this section entirely.977        let only_rlib = tcx.crate_types().iter().all(|ct| *ct == CrateType::Rlib);978        if only_rlib {979            info!("panic runtime injection skipped, only generating rlib");980            return;981        }982983        // If we need a panic runtime, we try to find an existing one here. At984        // the same time we perform some general validation of the DAG we've got985        // going such as ensuring everything has a compatible panic strategy.986        let mut needs_panic_runtime = attr::contains_name(&krate.attrs, sym::needs_panic_runtime);987        for (_cnum, data) in self.iter_crate_data() {988            needs_panic_runtime |= data.needs_panic_runtime();989        }990991        // If we just don't need a panic runtime at all, then we're done here992        // and there's nothing else to do.993        if !needs_panic_runtime {994            return;995        }996997        // By this point we know that we need a panic runtime. Here we just load998        // an appropriate default runtime for our panic strategy.999        //1000        // We may resolve to an already loaded crate (as the crate may not have1001        // been explicitly linked prior to this), but this is fine.1002        //1003        // Also note that we have yet to perform validation of the crate graph1004        // in terms of everyone has a compatible panic runtime format, that's1005        // performed later as part of the `dependency_format` module.1006        let desired_strategy = tcx.sess.panic_strategy();1007        let name = match desired_strategy {1008            PanicStrategy::Unwind => sym::panic_unwind,1009            PanicStrategy::Abort => sym::panic_abort,1010            PanicStrategy::ImmediateAbort => {1011                // Immediate-aborting panics don't use a runtime.1012                return;1013            }1014        };1015        info!("panic runtime not found -- loading {}", name);10161017        // This has to be conditional as both panic_unwind and panic_abort may be present in the1018        // crate graph at the same time. One of them will later be activated in dependency_formats.1019        let Some(cnum) = self.resolve_crate(1020            tcx,1021            name,1022            DUMMY_SP,1023            CrateDepKind::Conditional,1024            CrateOrigin::Injected,1025        ) else {1026            return;1027        };1028        let cdata = self.get_crate_data(cnum);10291030        // Sanity check the loaded crate to ensure it is indeed a panic runtime1031        // and the panic strategy is indeed what we thought it was.1032        if !cdata.is_panic_runtime() {1033            tcx.dcx().emit_err(errors::CrateNotPanicRuntime { crate_name: name });1034        }1035        if cdata.required_panic_strategy() != Some(desired_strategy) {1036            tcx.dcx()1037                .emit_err(errors::NoPanicStrategy { crate_name: name, strategy: desired_strategy });1038        }10391040        self.injected_panic_runtime = Some(cnum);1041    }10421043    fn inject_profiler_runtime(&mut self, tcx: TyCtxt<'_>) {1044        let needs_profiler_runtime =1045            tcx.sess.instrument_coverage() || tcx.sess.opts.cg.profile_generate.enabled();1046        if !needs_profiler_runtime || tcx.sess.opts.unstable_opts.no_profiler_runtime {1047            return;1048        }10491050        info!("loading profiler");10511052        // HACK: This uses conditional despite actually being unconditional to ensure that1053        // there is no error emitted when two dylibs independently depend on profiler_builtins.1054        // This is fine as profiler_builtins is always statically linked into the dylib just1055        // like compiler_builtins. Unlike compiler_builtins however there is no guaranteed1056        // common dylib that the duplicate crate check believes the crate to be included in.1057        // add_upstream_rust_crates has a corresponding check that forces profiler_builtins1058        // to be statically linked in even when marked as NotLinked.1059        let name = Symbol::intern(&tcx.sess.opts.unstable_opts.profiler_runtime);1060        let Some(cnum) = self.resolve_crate(1061            tcx,1062            name,1063            DUMMY_SP,1064            CrateDepKind::Conditional,1065            CrateOrigin::Injected,1066        ) else {1067            return;1068        };1069        let cdata = self.get_crate_data(cnum);10701071        // Sanity check the loaded crate to ensure it is indeed a profiler runtime1072        if !cdata.is_profiler_runtime() {1073            tcx.dcx().emit_err(errors::NotProfilerRuntime { crate_name: name });1074        }1075    }10761077    fn inject_allocator_crate(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) {1078        self.has_global_allocator =1079            match &*fn_spans(krate, Symbol::intern(&global_fn_name(sym::alloc))) {1080                [span1, span2, ..] => {1081                    tcx.dcx()1082                        .emit_err(errors::NoMultipleGlobalAlloc { span2: *span2, span1: *span1 });1083                    true1084                }1085                spans => !spans.is_empty(),1086            };1087        let alloc_error_handler = Symbol::intern(&global_fn_name(ALLOC_ERROR_HANDLER));1088        self.has_alloc_error_handler = match &*fn_spans(krate, alloc_error_handler) {1089            [span1, span2, ..] => {1090                tcx.dcx()1091                    .emit_err(errors::NoMultipleAllocErrorHandler { span2: *span2, span1: *span1 });1092                true1093            }1094            spans => !spans.is_empty(),1095        };10961097        // Check to see if we actually need an allocator. This desire comes1098        // about through the `#![needs_allocator]` attribute and is typically1099        // written down in liballoc.1100        if !attr::contains_name(&krate.attrs, sym::needs_allocator)1101            && !self.iter_crate_data().any(|(_, data)| data.needs_allocator())1102        {1103            return;1104        }11051106        // At this point we've determined that we need an allocator. Let's see1107        // if our compilation session actually needs an allocator based on what1108        // we're emitting.1109        let all_rlib = tcx.crate_types().iter().all(|ct| matches!(*ct, CrateType::Rlib));1110        if all_rlib {1111            return;1112        }11131114        // Ok, we need an allocator. Not only that but we're actually going to1115        // create an artifact that needs one linked in. Let's go find the one1116        // that we're going to link in.1117        //1118        // First up we check for global allocators. Look at the crate graph here1119        // and see what's a global allocator, including if we ourselves are a1120        // global allocator.1121        #[allow(rustc::symbol_intern_string_literal)]1122        let this_crate = Symbol::intern("this crate");11231124        let mut global_allocator = self.has_global_allocator.then_some(this_crate);1125        for (_, data) in self.iter_crate_data() {1126            if data.has_global_allocator() {1127                match global_allocator {1128                    Some(other_crate) => {1129                        tcx.dcx().emit_err(errors::ConflictingGlobalAlloc {1130                            crate_name: data.name(),1131                            other_crate_name: other_crate,1132                        });1133                    }1134                    None => global_allocator = Some(data.name()),1135                }1136            }1137        }1138        let mut alloc_error_handler = self.has_alloc_error_handler.then_some(this_crate);1139        for (_, data) in self.iter_crate_data() {1140            if data.has_alloc_error_handler() {1141                match alloc_error_handler {1142                    Some(other_crate) => {1143                        tcx.dcx().emit_err(errors::ConflictingAllocErrorHandler {1144                            crate_name: data.name(),1145                            other_crate_name: other_crate,1146                        });1147                    }1148                    None => alloc_error_handler = Some(data.name()),1149                }1150            }1151        }11521153        if global_allocator.is_some() {1154            self.allocator_kind = Some(AllocatorKind::Global);1155        } else {1156            // Ok we haven't found a global allocator but we still need an1157            // allocator. At this point our allocator request is typically fulfilled1158            // by the standard library, denoted by the `#![default_lib_allocator]`1159            // attribute.1160            if !attr::contains_name(&krate.attrs, sym::default_lib_allocator)1161                && !self.iter_crate_data().any(|(_, data)| data.has_default_lib_allocator())1162            {1163                tcx.dcx().emit_err(errors::GlobalAllocRequired);1164            }1165            self.allocator_kind = Some(AllocatorKind::Default);1166        }11671168        if alloc_error_handler.is_some() {1169            self.alloc_error_handler_kind = Some(AllocatorKind::Global);1170        } else {1171            // The alloc crate provides a default allocation error handler if1172            // one isn't specified.1173            self.alloc_error_handler_kind = Some(AllocatorKind::Default);1174        }1175    }11761177    fn inject_forced_externs(&mut self, tcx: TyCtxt<'_>) {1178        for (name, entry) in tcx.sess.opts.externs.iter() {1179            if entry.force {1180                let name_interned = Symbol::intern(name);1181                if !self.used_extern_options.contains(&name_interned) {1182                    self.resolve_crate(1183                        tcx,1184                        name_interned,1185                        DUMMY_SP,1186                        CrateDepKind::Unconditional,1187                        CrateOrigin::Extern,1188                    );1189                }1190            }1191        }1192    }11931194    /// Inject the `compiler_builtins` crate if it is not already in the graph.1195    fn inject_compiler_builtins(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) {1196        // `compiler_builtins` does not get extern builtins, nor do `#![no_core]` crates1197        if attr::contains_name(&krate.attrs, sym::compiler_builtins)1198            || attr::contains_name(&krate.attrs, sym::no_core)1199        {1200            info!("`compiler_builtins` unneeded");1201            return;1202        }12031204        // If a `#![compiler_builtins]` crate already exists, avoid injecting it twice. This is1205        // the common case since usually it appears as a dependency of `std` or `alloc`.1206        for (cnum, cmeta) in self.iter_crate_data() {1207            if cmeta.is_compiler_builtins() {1208                info!("`compiler_builtins` already exists (cnum = {cnum}); skipping injection");1209                return;1210            }1211        }12121213        // `compiler_builtins` is not yet in the graph; inject it. Error on resolution failure.1214        let Some(cnum) = self.resolve_crate(1215            tcx,1216            sym::compiler_builtins,1217            krate.spans.inner_span.shrink_to_lo(),1218            CrateDepKind::Unconditional,1219            CrateOrigin::Injected,1220        ) else {1221            info!("`compiler_builtins` not resolved");1222            return;1223        };12241225        // Sanity check that the loaded crate is `#![compiler_builtins]`1226        let cdata = self.get_crate_data(cnum);1227        if !cdata.is_compiler_builtins() {1228            tcx.dcx().emit_err(errors::CrateNotCompilerBuiltins { crate_name: cdata.name() });1229        }1230    }12311232    fn report_unused_deps_in_crate(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) {1233        // Make a point span rather than covering the whole file1234        let span = krate.spans.inner_span.shrink_to_lo();1235        // Complain about anything left over1236        for (name, entry) in tcx.sess.opts.externs.iter() {1237            if let ExternLocation::FoundInLibrarySearchDirectories = entry.location {1238                // Don't worry about pathless `--extern foo` sysroot references1239                continue;1240            }1241            if entry.nounused_dep || entry.force {1242                // We're not worried about this one1243                continue;1244            }1245            let name_interned = Symbol::intern(name);1246            if self.used_extern_options.contains(&name_interned) {1247                continue;1248            }12491250            // Got a real unused --extern1251            if tcx.sess.opts.json_unused_externs.is_enabled() {1252                self.unused_externs.push(name_interned);1253                continue;1254            }12551256            tcx.sess.psess.buffer_lint(1257                lint::builtin::UNUSED_CRATE_DEPENDENCIES,1258                span,1259                ast::CRATE_NODE_ID,1260                errors::UnusedCrateDependency {1261                    extern_crate: name_interned,1262                    local_crate: tcx.crate_name(LOCAL_CRATE),1263                },1264            );1265        }1266    }12671268    fn report_future_incompatible_deps(&self, tcx: TyCtxt<'_>, krate: &ast::Crate) {1269        let name = tcx.crate_name(LOCAL_CRATE);12701271        if name.as_str() == "wasm_bindgen" {1272            let major = env::var("CARGO_PKG_VERSION_MAJOR")1273                .ok()1274                .and_then(|major| u64::from_str(&major).ok());1275            let minor = env::var("CARGO_PKG_VERSION_MINOR")1276                .ok()1277                .and_then(|minor| u64::from_str(&minor).ok());1278            let patch = env::var("CARGO_PKG_VERSION_PATCH")1279                .ok()1280                .and_then(|patch| u64::from_str(&patch).ok());12811282            match (major, minor, patch) {1283                // v1 or bigger is valid.1284                (Some(1..), _, _) => return,1285                // v0.3 or bigger is valid.1286                (Some(0), Some(3..), _) => return,1287                // v0.2.88 or bigger is valid.1288                (Some(0), Some(2), Some(88..)) => return,1289                // Not using Cargo.1290                (None, None, None) => return,1291                _ => (),1292            }12931294            // Make a point span rather than covering the whole file1295            let span = krate.spans.inner_span.shrink_to_lo();12961297            tcx.sess.dcx().emit_err(errors::WasmCAbi { span });1298        }1299    }13001301    pub fn postprocess(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) {1302        self.inject_compiler_builtins(tcx, krate);1303        self.inject_forced_externs(tcx);1304        self.inject_profiler_runtime(tcx);1305        self.inject_allocator_crate(tcx, krate);1306        self.inject_panic_runtime(tcx, krate);13071308        self.report_unused_deps_in_crate(tcx, krate);1309        self.report_future_incompatible_deps(tcx, krate);13101311        info!("{:?}", CrateDump(self));1312    }13131314    /// Process an `extern crate foo` AST node.1315    pub fn process_extern_crate(1316        &mut self,1317        tcx: TyCtxt<'_>,1318        item: &ast::Item,1319        def_id: LocalDefId,1320        definitions: &Definitions,1321    ) -> Option<CrateNum> {1322        match item.kind {1323            ast::ItemKind::ExternCrate(orig_name, ident) => {1324                debug!("resolving extern crate stmt. ident: {} orig_name: {:?}", ident, orig_name);1325                let name = match orig_name {1326                    Some(orig_name) => {1327                        validate_crate_name(tcx.sess, orig_name, Some(item.span));1328                        orig_name1329                    }1330                    None => ident.name,1331                };1332                let dep_kind = if attr::contains_name(&item.attrs, sym::no_link) {1333                    CrateDepKind::MacrosOnly1334                } else {1335                    CrateDepKind::Unconditional1336                };13371338                let cnum =1339                    self.resolve_crate(tcx, name, item.span, dep_kind, CrateOrigin::Extern)?;13401341                let path_len = definitions.def_path(def_id).data.len();1342                self.update_extern_crate(1343                    cnum,1344                    name,1345                    ExternCrate {1346                        src: ExternCrateSource::Extern(def_id.to_def_id()),1347                        span: item.span,1348                        path_len,1349                        dependency_of: LOCAL_CRATE,1350                    },1351                );1352                Some(cnum)1353            }1354            _ => bug!(),1355        }1356    }13571358    pub fn process_path_extern(1359        &mut self,1360        tcx: TyCtxt<'_>,1361        name: Symbol,1362        span: Span,1363    ) -> Option<CrateNum> {1364        let cnum =1365            self.resolve_crate(tcx, name, span, CrateDepKind::Unconditional, CrateOrigin::Extern)?;13661367        self.update_extern_crate(1368            cnum,1369            name,1370            ExternCrate {1371                src: ExternCrateSource::Path,1372                span,1373                // to have the least priority in `update_extern_crate`1374                path_len: usize::MAX,1375                dependency_of: LOCAL_CRATE,1376            },1377        );13781379        Some(cnum)1380    }13811382    pub fn maybe_process_path_extern(&mut self, tcx: TyCtxt<'_>, name: Symbol) -> Option<CrateNum> {1383        self.maybe_resolve_crate(tcx, name, CrateDepKind::Unconditional, CrateOrigin::Extern).ok()1384    }1385}13861387fn fn_spans(krate: &ast::Crate, name: Symbol) -> Vec<Span> {1388    struct Finder {1389        name: Symbol,1390        spans: Vec<Span>,1391    }1392    impl<'ast> visit::Visitor<'ast> for Finder {1393        fn visit_item(&mut self, item: &'ast ast::Item) {1394            if let Some(ident) = item.kind.ident()1395                && ident.name == self.name1396                && attr::contains_name(&item.attrs, sym::rustc_std_internal_symbol)1397            {1398                self.spans.push(item.span);1399            }1400            visit::walk_item(self, item)1401        }1402    }14031404    let mut f = Finder { name, spans: Vec::new() };1405    visit::walk_crate(&mut f, krate);1406    f.spans1407}14081409fn format_dlopen_err(e: &(dyn std::error::Error + 'static)) -> String {1410    e.sources().map(|e| format!(": {e}")).collect()1411}14121413fn attempt_load_dylib(path: &Path) -> Result<libloading::Library, libloading::Error> {1414    #[cfg(target_os = "aix")]1415    if let Some(ext) = path.extension()1416        && ext.eq("a")1417    {1418        // On AIX, we ship all libraries as .a big_af archive1419        // the expected format is lib<name>.a(libname.so) for the actual1420        // dynamic library1421        let library_name = path.file_stem().expect("expect a library name");1422        let mut archive_member = std::ffi::OsString::from("a(");1423        archive_member.push(library_name);1424        archive_member.push(".so)");1425        let new_path = path.with_extension(archive_member);14261427        // On AIX, we need RTLD_MEMBER to dlopen an archived shared1428        let flags = libc::RTLD_LAZY | libc::RTLD_LOCAL | libc::RTLD_MEMBER;1429        return unsafe { libloading::os::unix::Library::open(Some(&new_path), flags) }1430            .map(|lib| lib.into());1431    }14321433    unsafe { libloading::Library::new(&path) }1434}14351436// On Windows the compiler would sometimes intermittently fail to open the1437// proc-macro DLL with `Error::LoadLibraryExW`. It is suspected that something in the1438// system still holds a lock on the file, so we retry a few times before calling it1439// an error.1440fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, String> {1441    assert!(max_attempts > 0);14421443    let mut last_error = None;14441445    for attempt in 0..max_attempts {1446        debug!("Attempt to load proc-macro `{}`.", path.display());1447        match attempt_load_dylib(path) {1448            Ok(lib) => {1449                if attempt > 0 {1450                    debug!(1451                        "Loaded proc-macro `{}` after {} attempts.",1452                        path.display(),1453                        attempt + 11454                    );1455                }1456                return Ok(lib);1457            }1458            Err(err) => {1459                // Only try to recover from this specific error.1460                if !matches!(err, libloading::Error::LoadLibraryExW { .. }) {1461                    debug!("Failed to load proc-macro `{}`. Not retrying", path.display());1462                    let err = format_dlopen_err(&err);1463                    // We include the path of the dylib in the error ourselves, so1464                    // if it's in the error, we strip it.1465                    if let Some(err) = err.strip_prefix(&format!(": {}", path.display())) {1466                        return Err(err.to_string());1467                    }1468                    return Err(err);1469                }14701471                last_error = Some(err);1472                std::thread::sleep(Duration::from_millis(100));1473                debug!("Failed to load proc-macro `{}`. Retrying.", path.display());1474            }1475        }1476    }14771478    debug!("Failed to load proc-macro `{}` even after {} attempts.", path.display(), max_attempts);14791480    let last_error = last_error.unwrap();1481    let message = if let Some(src) = last_error.source() {1482        format!("{} ({src}) (retried {max_attempts} times)", format_dlopen_err(&last_error))1483    } else {1484        format!("{} (retried {max_attempts} times)", format_dlopen_err(&last_error))1485    };1486    Err(message)1487}14881489pub enum DylibError {1490    DlOpen(String, String),1491    DlSym(String, String),1492}14931494impl From<DylibError> for CrateError {1495    fn from(err: DylibError) -> CrateError {1496        match err {1497            DylibError::DlOpen(path, err) => CrateError::DlOpen(path, err),1498            DylibError::DlSym(path, err) => CrateError::DlSym(path, err),1499        }1500    }1501}15021503pub unsafe fn load_symbol_from_dylib<T: Copy>(1504    path: &Path,1505    sym_name: &str,1506) -> Result<T, DylibError> {1507    // Make sure the path contains a / or the linker will search for it.1508    let path = try_canonicalize(path).unwrap();1509    let lib =1510        load_dylib(&path, 5).map_err(|err| DylibError::DlOpen(path.display().to_string(), err))?;15111512    let sym = unsafe { lib.get::<T>(sym_name.as_bytes()) }1513        .map_err(|err| DylibError::DlSym(path.display().to_string(), format_dlopen_err(&err)))?;15141515    // Intentionally leak the dynamic library. We can't ever unload it1516    // since the library can make things that will live arbitrarily long.1517    let sym = unsafe { sym.into_raw() };1518    std::mem::forget(lib);15191520    Ok(*sym)1521}

Code quality findings 31

Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
return unsafe { libloading::os::unix::Library::open(Some(&new_path), flags) }
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
unsafe { libloading::Library::new(&path) }
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
pub unsafe fn load_symbol_from_dylib<T: Copy>(
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let sym = unsafe { lib.get::<T>(sym_name.as_bytes()) }
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
let sym = unsafe { sym.into_raw() };
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
cstore.as_any().downcast_ref::<CStore>().expect("`tcx.cstore` is not a `CStore`")
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
cstore.untracked_as_any().downcast_mut().expect("`tcx.cstore` is not a `CStore`")
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
} else if let Some(crate_name1) = self.metas[existing].as_ref().map(|data| data.name())
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
self.metas[cnum].is_some()
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
self.metas[cnum].as_ref().unwrap_or_else(|| panic!("Failed to get crate data for {cnum:?}"))
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
self.metas[cnum].as_mut().unwrap_or_else(|| panic!("Failed to get crate data for {cnum:?}"))
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
assert!(self.metas[cnum].is_none(), "Overwriting crate metadata entry");
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
self.metas[cnum] = Some(Box::new(data));
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
let dlsym_dylib = dlsym_source.dylib.as_ref().expect("no dylib for a proc-macro crate");
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
Ok(Some((target_result.unwrap(), Some(host_result))))
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let extra_filename = dep.map(|d| &d.extra_filename[..]);
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
) -> Result<&'static [ProcMacro], CrateError> {
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
let library_name = path.file_stem().expect("expect a library name");
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 last_error = last_error.unwrap();
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 path = try_canonicalize(path).unwrap();
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 self {
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 self {
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 self {
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
local_value: local_value.to_string(),
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
flag_name: flag_name.clone(),
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
crate_num_map.push(krate);
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
let dep_kind = match dep_kind {
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
let result = load_symbol_from_dylib::<*const &[ProcMacro]>(path, &sym_name);
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(rustc::symbol_intern_string_literal)]
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 (major, minor, patch) {

Get this view in your editor

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