#
cargo/util/toml/
mod.rs

    1use annotate_snippets::{AnnotationKind, Group, Level, Snippet};
    2use std::borrow::Cow;
    3use std::cell::OnceCell;
    4use std::collections::{BTreeMap, BTreeSet, HashMap};
    5use std::ffi::OsStr;
    6use std::path::{Path, PathBuf};
    7use std::rc::Rc;
    8use std::str::{self, FromStr};
    9
    10use crate::AlreadyPrintedError;
    11use crate::core::summary::MissingDependencyError;
    12use anyhow::{Context as _, anyhow, bail};
    13use cargo_platform::Platform;
    14use cargo_util::paths;
    15use cargo_util_schemas::manifest::{
    16    self, PackageName, PathBaseName, TomlDependency, TomlDetailedDependency, TomlManifest,
    17    TomlPackageBuild, TomlWorkspace,
    18};
    19use cargo_util_schemas::manifest::{RustVersion, StringOrBool};
    20use itertools::Itertools;
    21use pathdiff::diff_paths;
    22use url::Url;
    23
    24use crate::core::compiler::{CompileKind, CompileTarget};
    25use crate::core::dependency::{Artifact, ArtifactTarget, DepKind};
    26use crate::core::manifest::{ManifestMetadata, TargetSourcePath};
    27use crate::core::resolver::ResolveBehavior;
    28use crate::core::{
    29    CliUnstable, FeatureValue, Patch, PatchLocation, find_workspace_root, resolve_relative_path,
    30};
    31use crate::core::{Dependency, Manifest, Package, PackageId, Summary, Target};
    32use crate::core::{Edition, EitherManifest, Feature, Features, VirtualManifest, Workspace};
    33use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, WorkspaceRootConfig};
    34use crate::lints::get_key_value_span;
    35use crate::lints::rel_cwd_manifest_path;
    36use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY};
    37use crate::util::errors::{CargoResult, ManifestError};
    38use crate::util::interning::InternedString;
    39use crate::util::{
    40    self, GlobalContext, IntoUrl, OnceExt, OptVersionReq, context::ConfigRelativePath,
    41    context::TOP_LEVEL_CONFIG_KEYS,
    42};
    43
    44mod embedded;
    45mod targets;
    46
    47use self::targets::to_targets;
    48
    49/// See also `bin/cargo/commands/run.rs`s `is_manifest_command`
    50pub fn is_embedded(path: &Path) -> bool {
    51    let ext = path.extension();
    52    (ext == Some(OsStr::new("rs")) ||
    53        // Provide better errors by not considering directories to be embedded manifests
    54        ext.is_none())
    55        && path.is_file()
    56}
    57
    58/// Loads a `Cargo.toml` from a file on disk.
    59///
    60/// This could result in a real or virtual manifest being returned.
    61///
    62/// A list of nested paths is also returned, one for each path dependency
    63/// within the manifest. For virtual manifests, these paths can only
    64/// come from patched or replaced dependencies. These paths are not
    65/// canonicalized.
    66#[tracing::instrument(skip(gctx))]
    67pub fn read_manifest(
    68    path: &Path,
    69    source_id: SourceId,
    70    gctx: &GlobalContext,
    71) -> CargoResult<EitherManifest> {
    72    let mut warnings = Default::default();
    73    let mut errors = Default::default();
    74
    75    let is_embedded = is_embedded(path);
    76    let contents = read_toml_string(path, is_embedded, gctx)?;
    77    let document = parse_document(&contents)
    78        .map_err(|e| emit_toml_diagnostic(e.into(), &contents, path, gctx))?;
    79    let original_toml = deserialize_toml(&document)
    80        .map_err(|e| emit_toml_diagnostic(e.into(), &contents, path, gctx))?;
    81
    82    let mut manifest = (|| {
    83        let empty = Vec::new();
    84        let cargo_features = original_toml.cargo_features.as_ref().unwrap_or(&empty);
    85        let features = Features::new(cargo_features, gctx, &mut warnings, source_id.is_path())?;
    86        let workspace_config =
    87            to_workspace_config(&original_toml, path, is_embedded, gctx, &mut warnings)?;
    88        if let WorkspaceConfig::Root(ws_root_config) = &workspace_config {
    89            let package_root = path.parent().unwrap();
    90            gctx.ws_roots()
    91                .insert(package_root.to_owned(), ws_root_config.clone());
    92        }
    93        let normalized_toml = normalize_toml(
    94            &original_toml,
    95            &features,
    96            &workspace_config,
    97            path,
    98            is_embedded,
    99            gctx,
    100            &mut warnings,
    101            &mut errors,
    102        )?;
    103
    104        if normalized_toml.package().is_some() {
    105            to_real_manifest(
    106                Some(contents),
    107                Some(document),
    108                original_toml,
    109                normalized_toml,
    110                features,
    111                workspace_config,
    112                source_id,
    113                path,
    114                is_embedded,
    115                gctx,
    116                &mut warnings,
    117                &mut errors,
    118            )
    119            .map(EitherManifest::Real)
    120        } else if normalized_toml.workspace.is_some() {
    121            assert!(!is_embedded);
    122            to_virtual_manifest(
    123                Some(contents),
    124                Some(document),
    125                original_toml,
    126                normalized_toml,
    127                features,
    128                workspace_config,
    129                source_id,
    130                path,
    131                gctx,
    132                &mut warnings,
    133                &mut errors,
    134            )
    135            .map(EitherManifest::Virtual)
    136        } else {
    137            anyhow::bail!("manifest is missing either a `[package]` or a `[workspace]`")
    138        }
    139    })()
    140    .map_err(|err| {
    141        ManifestError::new(
    142            err.context(format!("failed to parse manifest at `{}`", path.display())),
    143            path.into(),
    144        )
    145    })?;
    146
    147    for warning in warnings {
    148        manifest.warnings_mut().add_warning(warning);
    149    }
    150    for error in errors {
    151        manifest.warnings_mut().add_critical_warning(error);
    152    }
    153
    154    Ok(manifest)
    155}
    156
    157#[tracing::instrument(skip_all)]
    158fn read_toml_string(path: &Path, is_embedded: bool, gctx: &GlobalContext) -> CargoResult<String> {
    159    let mut contents = paths::read(path).map_err(|err| ManifestError::new(err, path.into()))?;
    160    if is_embedded {
    161        if !gctx.cli_unstable().script {
    162            anyhow::bail!("parsing `{}` requires `-Zscript`", path.display());
    163        }
    164        contents = embedded::expand_manifest(&contents)
    165            .map_err(|e| emit_frontmatter_diagnostic(e, &contents, path, gctx))?;
    166    }
    167    Ok(contents)
    168}
    169
    170#[tracing::instrument(skip_all)]
    171fn parse_document(
    172    contents: &str,
    173) -> Result<toml::Spanned<toml::de::DeTable<'static>>, toml::de::Error> {
    174    let mut table = toml::de::DeTable::parse(contents)?;
    175    table.get_mut().make_owned();
    176    // SAFETY: `DeTable::make_owned` ensures no borrows remain and the lifetime does not affect
    177    // layout
    178    let table = unsafe {
    179        std::mem::transmute::<
    180            toml::Spanned<toml::de::DeTable<'_>>,
    181            toml::Spanned<toml::de::DeTable<'static>>,
    182        >(table)
    183    };
    184    Ok(table)
    185}
    186
    187#[tracing::instrument(skip_all)]
    188fn deserialize_toml(
    189    document: &toml::Spanned<toml::de::DeTable<'static>>,
    190) -> Result<manifest::TomlManifest, toml::de::Error> {
    191    let mut unused = BTreeSet::new();
    192    let deserializer = toml::de::Deserializer::from(document.clone());
    193    let mut document: manifest::TomlManifest = serde_ignored::deserialize(deserializer, |path| {
    194        let mut key = String::new();
    195        stringify(&mut key, &path);
    196        unused.insert(key);
    197    })?;
    198    document._unused_keys = unused;
    199    Ok(document)
    200}
    201
    202fn stringify(dst: &mut String, path: &serde_ignored::Path<'_>) {
    203    use serde_ignored::Path;
    204
    205    match *path {
    206        Path::Root => {}
    207        Path::Seq { parent, index } => {
    208            stringify(dst, parent);
    209            if !dst.is_empty() {
    210                dst.push('.');
    211            }
    212            dst.push_str(&index.to_string());
    213        }
    214        Path::Map { parent, ref key } => {
    215            stringify(dst, parent);
    216            if !dst.is_empty() {
    217                dst.push('.');
    218            }
    219            dst.push_str(key);
    220        }
    221        Path::Some { parent }
    222        | Path::NewtypeVariant { parent }
    223        | Path::NewtypeStruct { parent } => stringify(dst, parent),
    224    }
    225}
    226
    227fn to_workspace_config(
    228    original_toml: &manifest::TomlManifest,
    229    manifest_file: &Path,
    230    is_embedded: bool,
    231    gctx: &GlobalContext,
    232    warnings: &mut Vec<String>,
    233) -> CargoResult<WorkspaceConfig> {
    234    if is_embedded {
    235        let ws_root_config = to_workspace_root_config(&TomlWorkspace::default(), manifest_file);
    236        return Ok(WorkspaceConfig::Root(ws_root_config));
    237    }
    238    let workspace_config = match (
    239        original_toml.workspace.as_ref(),
    240        original_toml.package().and_then(|p| p.workspace.as_ref()),
    241    ) {
    242        (Some(toml_config), None) => {
    243            verify_lints(toml_config.lints.as_ref(), gctx, warnings)?;
    244            if let Some(ws_deps) = &toml_config.dependencies {
    245                for (name, dep) in ws_deps {
    246                    if dep.is_optional() {
    247                        bail!("{name} is optional, but workspace dependencies cannot be optional",);
    248                    }
    249                    if dep.is_public() {
    250                        bail!("{name} is public, but workspace dependencies cannot be public",);
    251                    }
    252                }
    253
    254                for (name, dep) in ws_deps {
    255                    unused_dep_keys(name, "workspace.dependencies", dep.unused_keys(), warnings);
    256                }
    257            }
    258            let ws_root_config = to_workspace_root_config(toml_config, manifest_file);
    259            WorkspaceConfig::Root(ws_root_config)
    260        }
    261        (None, root) => WorkspaceConfig::Member {
    262            root: root.cloned(),
    263        },
    264        (Some(..), Some(..)) => bail!(
    265            "cannot configure both `package.workspace` and \
    266                 `[workspace]`, only one can be specified"
    267        ),
    268    };
    269    Ok(workspace_config)
    270}
    271
    272fn to_workspace_root_config(
    273    normalized_toml: &manifest::TomlWorkspace,
    274    manifest_file: &Path,
    275) -> WorkspaceRootConfig {
    276    let package_root = manifest_file.parent().unwrap();
    277    let inheritable = InheritableFields {
    278        package: normalized_toml.package.clone(),
    279        dependencies: normalized_toml.dependencies.clone(),
    280        lints: normalized_toml.lints.clone(),
    281        _ws_root: package_root.to_owned(),
    282    };
    283    let ws_root_config = WorkspaceRootConfig::new(
    284        package_root,
    285        &normalized_toml.members,
    286        &normalized_toml.default_members,
    287        &normalized_toml.exclude,
    288        &Some(inheritable),
    289        &normalized_toml.metadata,
    290    );
    291    ws_root_config
    292}
    293
    294/// See [`Manifest::normalized_toml`] for more details
    295#[tracing::instrument(skip_all)]
    296fn normalize_toml(
    297    original_toml: &manifest::TomlManifest,
    298    features: &Features,
    299    workspace_config: &WorkspaceConfig,
    300    manifest_file: &Path,
    301    is_embedded: bool,
    302    gctx: &GlobalContext,
    303    warnings: &mut Vec<String>,
    304    errors: &mut Vec<String>,
    305) -> CargoResult<manifest::TomlManifest> {
    306    let package_root = manifest_file.parent().unwrap();
    307
    308    let inherit_cell: OnceCell<InheritableFields> = OnceCell::new();
    309    let inherit = || {
    310        inherit_cell
    311            .try_borrow_with(|| load_inheritable_fields(gctx, manifest_file, &workspace_config))
    312    };
    313    let workspace_root = || inherit().map(|fields| fields.ws_root().as_path());
    314
    315    let mut normalized_toml = manifest::TomlManifest {
    316        cargo_features: original_toml.cargo_features.clone(),
    317        package: None,
    318        project: None,
    319        badges: None,
    320        features: None,
    321        lib: None,
    322        bin: None,
    323        example: None,
    324        test: None,
    325        bench: None,
    326        dependencies: None,
    327        dev_dependencies: None,
    328        dev_dependencies2: None,
    329        build_dependencies: None,
    330        build_dependencies2: None,
    331        target: None,
    332        lints: None,
    333        hints: None,
    334        workspace: original_toml.workspace.clone().or_else(|| {
    335            // Prevent looking for a workspace by `read_manifest_from_str`
    336            is_embedded.then(manifest::TomlWorkspace::default)
    337        }),
    338        profile: original_toml.profile.clone(),
    339        patch: normalize_patch(
    340            gctx,
    341            original_toml.patch.as_ref(),
    342            &workspace_root,
    343            features,
    344        )?,
    345        replace: original_toml.replace.clone(),
    346        _unused_keys: Default::default(),
    347    };
    348
    349    if let Some(original_package) = original_toml.package().map(Cow::Borrowed).or_else(|| {
    350        if is_embedded {
    351            Some(Cow::Owned(Box::new(manifest::TomlPackage::default())))
    352        } else {
    353            None
    354        }
    355    }) {
    356        let normalized_package = normalize_package_toml(
    357            &original_package,
    358            manifest_file,
    359            is_embedded,
    360            gctx,
    361            &inherit,
    362            features,
    363        )?;
    364        let package_name = &normalized_package
    365            .normalized_name()
    366            .expect("previously normalized")
    367            .clone();
    368        let edition = normalized_package
    369            .normalized_edition()
    370            .expect("previously normalized")
    371            .map_or(Edition::default(), |e| {
    372                Edition::from_str(&e).unwrap_or_default()
    373            });
    374        normalized_toml.package = Some(normalized_package);
    375
    376        normalized_toml.features = normalize_features(original_toml.features.as_ref())?;
    377
    378        let auto_embedded = is_embedded.then_some(false);
    379        normalized_toml.lib = targets::normalize_lib(
    380            original_toml.lib.as_ref(),
    381            package_root,
    382            package_name,
    383            edition,
    384            original_package.autolib.or(auto_embedded),
    385            warnings,
    386        )?;
    387        let original_toml_bin = if is_embedded {
    388            let name = package_name.as_ref().to_owned();
    389            let manifest_file_name = manifest_file
    390                .file_name()
    391                .expect("file name enforced previously");
    392            let path = PathBuf::from(manifest_file_name);
    393            Cow::Owned(Some(vec![manifest::TomlBinTarget {
    394                name: Some(name),
    395                crate_type: None,
    396                crate_type2: None,
    397                path: Some(manifest::PathValue(path)),
    398                filename: None,
    399                test: None,
    400                doctest: None,
    401                bench: None,
    402                doc: None,
    403                doc_scrape_examples: None,
    404                proc_macro: None,
    405                proc_macro2: None,
    406                harness: None,
    407                required_features: None,
    408                edition: None,
    409            }]))
    410        } else {
    411            Cow::Borrowed(&original_toml.bin)
    412        };
    413        normalized_toml.bin = Some(targets::normalize_bins(
    414            original_toml_bin.as_ref().as_ref(),
    415            package_root,
    416            package_name,
    417            edition,
    418            original_package.autobins.or(auto_embedded),
    419            warnings,
    420            errors,
    421            normalized_toml.lib.is_some(),
    422        )?);
    423        normalized_toml.example = Some(targets::normalize_examples(
    424            original_toml.example.as_ref(),
    425            package_root,
    426            edition,
    427            original_package.autoexamples.or(auto_embedded),
    428            warnings,
    429            errors,
    430        )?);
    431        normalized_toml.test = Some(targets::normalize_tests(
    432            original_toml.test.as_ref(),
    433            package_root,
    434            edition,
    435            original_package.autotests.or(auto_embedded),
    436            warnings,
    437            errors,
    438        )?);
    439        normalized_toml.bench = Some(targets::normalize_benches(
    440            original_toml.bench.as_ref(),
    441            package_root,
    442            edition,
    443            original_package.autobenches.or(auto_embedded),
    444            warnings,
    445            errors,
    446        )?);
    447
    448        normalized_toml.dependencies = normalize_dependencies(
    449            gctx,
    450            edition,
    451            &features,
    452            original_toml.dependencies.as_ref(),
    453            DepKind::Normal,
    454            &inherit,
    455            &workspace_root,
    456            package_root,
    457            warnings,
    458        )?;
    459        deprecated_underscore(
    460            &original_toml.dev_dependencies2,
    461            &original_toml.dev_dependencies,
    462            "dev-dependencies",
    463            package_name,
    464            "package",
    465            edition,
    466            warnings,
    467        )?;
    468        normalized_toml.dev_dependencies = normalize_dependencies(
    469            gctx,
    470            edition,
    471            &features,
    472            original_toml.dev_dependencies(),
    473            DepKind::Development,
    474            &inherit,
    475            &workspace_root,
    476            package_root,
    477            warnings,
    478        )?;
    479        deprecated_underscore(
    480            &original_toml.build_dependencies2,
    481            &original_toml.build_dependencies,
    482            "build-dependencies",
    483            package_name,
    484            "package",
    485            edition,
    486            warnings,
    487        )?;
    488        normalized_toml.build_dependencies = normalize_dependencies(
    489            gctx,
    490            edition,
    491            &features,
    492            original_toml.build_dependencies(),
    493            DepKind::Build,
    494            &inherit,
    495            &workspace_root,
    496            package_root,
    497            warnings,
    498        )?;
    499        let mut normalized_target = BTreeMap::new();
    500        for (name, platform) in original_toml.target.iter().flatten() {
    501            let normalized_dependencies = normalize_dependencies(
    502                gctx,
    503                edition,
    504                &features,
    505                platform.dependencies.as_ref(),
    506                DepKind::Normal,
    507                &inherit,
    508                &workspace_root,
    509                package_root,
    510                warnings,
    511            )?;
    512            deprecated_underscore(
    513                &platform.dev_dependencies2,
    514                &platform.dev_dependencies,
    515                "dev-dependencies",
    516                name,
    517                "platform target",
    518                edition,
    519                warnings,
    520            )?;
    521            let normalized_dev_dependencies = normalize_dependencies(
    522                gctx,
    523                edition,
    524                &features,
    525                platform.dev_dependencies(),
    526                DepKind::Development,
    527                &inherit,
    528                &workspace_root,
    529                package_root,
    530                warnings,
    531            )?;
    532            deprecated_underscore(
    533                &platform.build_dependencies2,
    534                &platform.build_dependencies,
    535                "build-dependencies",
    536                name,
    537                "platform target",
    538                edition,
    539                warnings,
    540            )?;
    541            let normalized_build_dependencies = normalize_dependencies(
    542                gctx,
    543                edition,
    544                &features,
    545                platform.build_dependencies(),
    546                DepKind::Build,
    547                &inherit,
    548                &workspace_root,
    549                package_root,
    550                warnings,
    551            )?;
    552            normalized_target.insert(
    553                name.clone(),
    554                manifest::TomlPlatform {
    555                    dependencies: normalized_dependencies,
    556                    build_dependencies: normalized_build_dependencies,
    557                    build_dependencies2: None,
    558                    dev_dependencies: normalized_dev_dependencies,
    559                    dev_dependencies2: None,
    560                },
    561            );
    562        }
    563        normalized_toml.target = (!normalized_target.is_empty()).then_some(normalized_target);
    564
    565        let normalized_lints = original_toml
    566            .lints
    567            .clone()
    568            .map(|value| lints_inherit_with(value, || inherit()?.lints()))
    569            .transpose()?;
    570        normalized_toml.lints = normalized_lints.map(|lints| manifest::InheritableLints {
    571            workspace: false,
    572            lints,
    573        });
    574
    575        normalized_toml.hints = original_toml.hints.clone();
    576
    577        normalized_toml.badges = original_toml.badges.clone();
    578    } else {
    579        if let Some(field) = original_toml.requires_package().next() {
    580            bail!("this virtual manifest specifies a `{field}` section, which is not allowed");
    581        }
    582    }
    583
    584    Ok(normalized_toml)
    585}
    586
    587fn normalize_patch<'a>(
    588    gctx: &GlobalContext,
    589    original_patch: Option<&BTreeMap<String, BTreeMap<PackageName, TomlDependency>>>,
    590    workspace_root: &dyn Fn() -> CargoResult<&'a Path>,
    591    features: &Features,
    592) -> CargoResult<Option<BTreeMap<String, BTreeMap<PackageName, TomlDependency>>>> {
    593    if let Some(patch) = original_patch {
    594        let mut normalized_patch = BTreeMap::new();
    595        for (name, packages) in patch {
    596            let mut normalized_packages = BTreeMap::new();
    597            for (pkg, dep) in packages {
    598                let dep = if let TomlDependency::Detailed(dep) = dep {
    599                    let mut dep = dep.clone();
    600                    normalize_path_dependency(gctx, &mut dep, workspace_root, features)
    601                        .with_context(|| {
    602                            format!("resolving path for patch of ({pkg}) for source ({name})")
    603                        })?;
    604                    TomlDependency::Detailed(dep)
    605                } else {
    606                    dep.clone()
    607                };
    608                normalized_packages.insert(pkg.clone(), dep);
    609            }
    610            normalized_patch.insert(name.clone(), normalized_packages);
    611        }
    612        Ok(Some(normalized_patch))
    613    } else {
    614        Ok(None)
    615    }
    616}
    617
    618#[tracing::instrument(skip_all)]
    619fn normalize_package_toml<'a>(
    620    original_package: &manifest::TomlPackage,
    621    manifest_file: &Path,
    622    is_embedded: bool,
    623    gctx: &GlobalContext,
    624    inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
    625    features: &Features,
    626) -> CargoResult<Box<manifest::TomlPackage>> {
    627    let package_root = manifest_file.parent().unwrap();
    628
    629    let edition = original_package
    630        .edition
    631        .clone()
    632        .map(|value| field_inherit_with(value, "edition", || inherit()?.edition()))
    633        .transpose()?
    634        .map(manifest::InheritableField::Value)
    635        .or_else(|| {
    636            if is_embedded {
    637                const DEFAULT_EDITION: crate::core::features::Edition =
    638                    crate::core::features::Edition::LATEST_STABLE;
    639                let _ = gctx.shell().warn(format_args!(
    640                    "`package.edition` is unspecified, defaulting to `{}`",
    641                    DEFAULT_EDITION
    642                ));
    643                Some(manifest::InheritableField::Value(
    644                    DEFAULT_EDITION.to_string(),
    645                ))
    646            } else {
    647                None
    648            }
    649        });
    650    let rust_version = original_package
    651        .rust_version
    652        .clone()
    653        .map(|value| field_inherit_with(value, "rust-version", || inherit()?.rust_version()))
    654        .transpose()?
    655        .map(manifest::InheritableField::Value);
    656    let name = Some(
    657        original_package
    658            .name
    659            .clone()
    660            .or_else(|| {
    661                if is_embedded {
    662                    let file_stem = manifest_file
    663                        .file_stem()
    664                        .expect("file name enforced previously")
    665                        .to_string_lossy();
    666                    let name = embedded::sanitize_name(file_stem.as_ref());
    667                    let name =
    668                        manifest::PackageName::new(name).expect("sanitize made the name valid");
    669                    Some(name)
    670                } else {
    671                    None
    672                }
    673            })
    674            .ok_or_else(|| anyhow::format_err!("missing field `package.name`"))?,
    675    );
    676    let version = original_package
    677        .version
    678        .clone()
    679        .map(|value| field_inherit_with(value, "version", || inherit()?.version()))
    680        .transpose()?
    681        .map(manifest::InheritableField::Value);
    682    let authors = original_package
    683        .authors
    684        .clone()
    685        .map(|value| field_inherit_with(value, "authors", || inherit()?.authors()))
    686        .transpose()?
    687        .map(manifest::InheritableField::Value);
    688    let build = if is_embedded {
    689        Some(TomlPackageBuild::Auto(false))
    690    } else {
    691        if let Some(TomlPackageBuild::MultipleScript(_)) = original_package.build {
    692            features.require(Feature::multiple_build_scripts())?;
    693        }
    694        targets::normalize_build(original_package.build.as_ref(), package_root)?
    695    };
    696    let metabuild = original_package.metabuild.clone();
    697    let default_target = original_package.default_target.clone();
    698    let forced_target = original_package.forced_target.clone();
    699    let links = original_package.links.clone();
    700    let exclude = original_package
    701        .exclude
    702        .clone()
    703        .map(|value| field_inherit_with(value, "exclude", || inherit()?.exclude()))
    704        .transpose()?
    705        .map(manifest::InheritableField::Value);
    706    let include = original_package
    707        .include
    708        .clone()
    709        .map(|value| field_inherit_with(value, "include", || inherit()?.include()))
    710        .transpose()?
    711        .map(manifest::InheritableField::Value);
    712    let publish = original_package
    713        .publish
    714        .clone()
    715        .map(|value| field_inherit_with(value, "publish", || inherit()?.publish()))
    716        .transpose()?
    717        .map(manifest::InheritableField::Value);
    718    let workspace = original_package.workspace.clone();
    719    let im_a_teapot = original_package.im_a_teapot.clone();
    720    let autolib = Some(false);
    721    let autobins = Some(false);
    722    let autoexamples = Some(false);
    723    let autotests = Some(false);
    724    let autobenches = Some(false);
    725    let default_run = original_package.default_run.clone();
    726    let description = original_package
    727        .description
    728        .clone()
    729        .map(|value| field_inherit_with(value, "description", || inherit()?.description()))
    730        .transpose()?
    731        .map(manifest::InheritableField::Value);
    732    let homepage = original_package
    733        .homepage
    734        .clone()
    735        .map(|value| field_inherit_with(value, "homepage", || inherit()?.homepage()))
    736        .transpose()?
    737        .map(manifest::InheritableField::Value);
    738    let documentation = original_package
    739        .documentation
    740        .clone()
    741        .map(|value| field_inherit_with(value, "documentation", || inherit()?.documentation()))
    742        .transpose()?
    743        .map(manifest::InheritableField::Value);
    744    let readme = normalize_package_readme(
    745        package_root,
    746        original_package
    747            .readme
    748            .clone()
    749            .map(|value| field_inherit_with(value, "readme", || inherit()?.readme(package_root)))
    750            .transpose()?
    751            .as_ref(),
    752    )
    753    .map(|s| manifest::InheritableField::Value(StringOrBool::String(s)))
    754    .or(Some(manifest::InheritableField::Value(StringOrBool::Bool(
    755        false,
    756    ))));
    757    let keywords = original_package
    758        .keywords
    759        .clone()
    760        .map(|value| field_inherit_with(value, "keywords", || inherit()?.keywords()))
    761        .transpose()?
    762        .map(manifest::InheritableField::Value);
    763    let categories = original_package
    764        .categories
    765        .clone()
    766        .map(|value| field_inherit_with(value, "categories", || inherit()?.categories()))
    767        .transpose()?
    768        .map(manifest::InheritableField::Value);
    769    let license = original_package
    770        .license
    771        .clone()
    772        .map(|value| field_inherit_with(value, "license", || inherit()?.license()))
    773        .transpose()?
    774        .map(manifest::InheritableField::Value);
    775    let license_file = original_package
    776        .license_file
    777        .clone()
    778        .map(|value| {
    779            field_inherit_with(value, "license-file", || {
    780                inherit()?.license_file(package_root)
    781            })
    782        })
    783        .transpose()?
    784        .map(manifest::InheritableField::Value);
    785    let repository = original_package
    786        .repository
    787        .clone()
    788        .map(|value| field_inherit_with(value, "repository", || inherit()?.repository()))
    789        .transpose()?
    790        .map(manifest::InheritableField::Value);
    791    let resolver = original_package.resolver.clone();
    792    let metadata = original_package.metadata.clone();
    793
    794    let normalized_package = manifest::TomlPackage {
    795        edition,
    796        rust_version,
    797        name,
    798        version,
    799        authors,
    800        build,
    801        metabuild,
    802        default_target,
    803        forced_target,
    804        links,
    805        exclude,
    806        include,
    807        publish,
    808        workspace,
    809        im_a_teapot,
    810        autolib,
    811        autobins,
    812        autoexamples,
    813        autotests,
    814        autobenches,
    815        default_run,
    816        description,
    817        homepage,
    818        documentation,
    819        readme,
    820        keywords,
    821        categories,
    822        license,
    823        license_file,
    824        repository,
    825        resolver,
    826        metadata,
    827        _invalid_cargo_features: Default::default(),
    828    };
    829
    830    Ok(Box::new(normalized_package))
    831}
    832
    833/// Returns the name of the README file for a [`manifest::TomlPackage`].
    834fn normalize_package_readme(
    835    package_root: &Path,
    836    readme: Option<&manifest::StringOrBool>,
    837) -> Option<String> {
    838    match &readme {
    839        None => default_readme_from_package_root(package_root),
    840        Some(value) => match value {
    841            manifest::StringOrBool::Bool(false) => None,
    842            manifest::StringOrBool::Bool(true) => Some("README.md".to_string()),
    843            manifest::StringOrBool::String(v) => Some(v.clone()),
    844        },
    845    }
    846}
    847
    848const DEFAULT_README_FILES: [&str; 3] = ["README.md", "README.txt", "README"];
    849
    850/// Checks if a file with any of the default README file names exists in the package root.
    851/// If so, returns a `String` representing that name.
    852fn default_readme_from_package_root(package_root: &Path) -> Option<String> {
    853    for &readme_filename in DEFAULT_README_FILES.iter() {
    854        if package_root.join(readme_filename).is_file() {
    855            return Some(readme_filename.to_string());
    856        }
    857    }
    858
    859    None
    860}
    861
    862#[tracing::instrument(skip_all)]
    863fn normalize_features(
    864    original_features: Option<&BTreeMap<manifest::FeatureName, Vec<String>>>,
    865) -> CargoResult<Option<BTreeMap<manifest::FeatureName, Vec<String>>>> {
    866    let Some(normalized_features) = original_features.cloned() else {
    867        return Ok(None);
    868    };
    869
    870    Ok(Some(normalized_features))
    871}
    872
    873#[tracing::instrument(skip_all)]
    874fn normalize_dependencies<'a>(
    875    gctx: &GlobalContext,
    876    edition: Edition,
    877    features: &Features,
    878    orig_deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
    879    kind: DepKind,
    880    inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
    881    workspace_root: &dyn Fn() -> CargoResult<&'a Path>,
    882    package_root: &Path,
    883    warnings: &mut Vec<String>,
    884) -> CargoResult<Option<BTreeMap<manifest::PackageName, manifest::InheritableDependency>>> {
    885    let Some(dependencies) = orig_deps else {
    886        return Ok(None);
    887    };
    888
    889    let mut deps = BTreeMap::new();
    890    for (name_in_toml, v) in dependencies.iter() {
    891        let mut resolved = dependency_inherit_with(
    892            v.clone(),
    893            name_in_toml,
    894            inherit,
    895            package_root,
    896            edition,
    897            warnings,
    898        )?;
    899        if let manifest::TomlDependency::Detailed(ref mut d) = resolved {
    900            deprecated_underscore(
    901                &d.default_features2,
    902                &d.default_features,
    903                "default-features",
    904                name_in_toml,
    905                "dependency",
    906                edition,
    907                warnings,
    908            )?;
    909            if d.public.is_some() {
    910                let with_public_feature = features.require(Feature::public_dependency()).is_ok();
    911                let with_z_public = gctx.cli_unstable().public_dependency;
    912                match kind {
    913                    DepKind::Normal => {
    914                        if !with_public_feature && !with_z_public {
    915                            d.public = None;
    916                            warnings.push(format!(
    917                                "ignoring `public` on dependency {name_in_toml}, pass `-Zpublic-dependency` to enable support for it"
    918                            ));
    919                        }
    920                    }
    921                    DepKind::Development | DepKind::Build => {
    922                        let kind_name = kind.kind_table();
    923                        let hint = format!(
    924                            "'public' specifier can only be used on regular dependencies, not {kind_name}",
    925                        );
    926                        if with_public_feature || with_z_public {
    927                            bail!(hint)
    928                        } else {
    929                            // If public feature isn't enabled in nightly, we instead warn that.
    930                            warnings.push(hint);
    931                            d.public = None;
    932                        }
    933                    }
    934                }
    935            }
    936            normalize_path_dependency(gctx, d, workspace_root, features)
    937                .with_context(|| format!("resolving path dependency {name_in_toml}"))?;
    938        }
    939
    940        deps.insert(
    941            name_in_toml.clone(),
    942            manifest::InheritableDependency::Value(resolved.clone()),
    943        );
    944    }
    945    Ok(Some(deps))
    946}
    947
    948fn normalize_path_dependency<'a>(
    949    gctx: &GlobalContext,
    950    detailed_dep: &mut TomlDetailedDependency,
    951    workspace_root: &dyn Fn() -> CargoResult<&'a Path>,
    952    features: &Features,
    953) -> CargoResult<()> {
    954    if let Some(base) = detailed_dep.base.take() {
    955        if let Some(path) = detailed_dep.path.as_mut() {
    956            let new_path = lookup_path_base(&base, gctx, workspace_root, features)?.join(&path);
    957            *path = new_path.to_str().unwrap().to_string();
    958        } else {
    959            bail!("`base` can only be used with path dependencies");
    960        }
    961    }
    962    Ok(())
    963}
    964
    965fn load_inheritable_fields(
    966    gctx: &GlobalContext,
    967    normalized_path: &Path,
    968    workspace_config: &WorkspaceConfig,
    969) -> CargoResult<InheritableFields> {
    970    match workspace_config {
    971        WorkspaceConfig::Root(root) => Ok(root.inheritable().clone()),
    972        WorkspaceConfig::Member {
    973            root: Some(path_to_root),
    974        } => {
    975            let path = normalized_path
    976                .parent()
    977                .unwrap()
    978                .join(path_to_root)
    979                .join("Cargo.toml");
    980            let root_path = paths::normalize_path(&path);
    981            inheritable_from_path(gctx, root_path)
    982        }
    983        WorkspaceConfig::Member { root: None } => {
    984            match find_workspace_root(&normalized_path, gctx)? {
    985                Some(path_to_root) => inheritable_from_path(gctx, path_to_root),
    986                None => Err(anyhow!("failed to find a workspace root")),
    987            }
    988        }
    989    }
    990}
    991
    992fn inheritable_from_path(
    993    gctx: &GlobalContext,
    994    workspace_path: PathBuf,
    995) -> CargoResult<InheritableFields> {
    996    // Workspace path should have Cargo.toml at the end
    997    let workspace_path_root = workspace_path.parent().unwrap();
    998
    999    // Let the borrow exit scope so that it can be picked up if there is a need to
    1000    // read a manifest
    1001    if let Some(ws_root) = gctx.ws_roots().get(workspace_path_root) {
    1002        return Ok(ws_root.inheritable().clone());
    1003    };
    1004
    1005    let source_id = SourceId::for_manifest_path(&workspace_path)?;
    1006    let man = read_manifest(&workspace_path, source_id, gctx)?;
    1007    match man.workspace_config() {
    1008        WorkspaceConfig::Root(root) => {
    1009            gctx.ws_roots().insert(workspace_path, root.clone());
    1010            Ok(root.inheritable().clone())
    1011        }
    1012        _ => bail!(
    1013            "root of a workspace inferred but wasn't a root: {}",
    1014            workspace_path.display()
    1015        ),
    1016    }
    1017}
    1018
    1019/// Defines simple getter methods for inheritable fields.
    1020macro_rules! package_field_getter {
    1021    ( $(($key:literal, $field:ident -> $ret:ty),)* ) => (
    1022        $(
    1023            #[doc = concat!("Gets the field `workspace.package.", $key, "`.")]
    1024            fn $field(&self) -> CargoResult<$ret> {
    1025                let Some(val) = self.package.as_ref().and_then(|p| p.$field.as_ref()) else  {
    1026                    bail!("`workspace.package.{}` was not defined", $key);
    1027                };
    1028                Ok(val.clone())
    1029            }
    1030        )*
    1031    )
    1032}
    1033
    1034/// A group of fields that are inheritable by members of the workspace
    1035#[derive(Clone, Debug, Default)]
    1036pub struct InheritableFields {
    1037    package: Option<manifest::InheritablePackage>,
    1038    dependencies: Option<BTreeMap<manifest::PackageName, manifest::TomlDependency>>,
    1039    lints: Option<manifest::TomlLints>,
    1040
    1041    // Bookkeeping to help when resolving values from above
    1042    _ws_root: PathBuf,
    1043}
    1044
    1045impl InheritableFields {
    1046    package_field_getter! {
    1047        // Please keep this list lexicographically ordered.
    1048        ("authors",       authors       -> Vec<String>),
    1049        ("categories",    categories    -> Vec<String>),
    1050        ("description",   description   -> String),
    1051        ("documentation", documentation -> String),
    1052        ("edition",       edition       -> String),
    1053        ("exclude",       exclude       -> Vec<String>),
    1054        ("homepage",      homepage      -> String),
    1055        ("include",       include       -> Vec<String>),
    1056        ("keywords",      keywords      -> Vec<String>),
    1057        ("license",       license       -> String),
    1058        ("publish",       publish       -> manifest::VecStringOrBool),
    1059        ("repository",    repository    -> String),
    1060        ("rust-version",  rust_version  -> RustVersion),
    1061        ("version",       version       -> semver::Version),
    1062    }
    1063
    1064    /// Gets a workspace dependency with the `name`.
    1065    fn get_dependency(
    1066        &self,
    1067        name: &str,
    1068        package_root: &Path,
    1069    ) -> CargoResult<manifest::TomlDependency> {
    1070        let Some(deps) = &self.dependencies else {
    1071            bail!("`workspace.dependencies` was not defined");
    1072        };
    1073        let Some(dep) = deps.get(name) else {
    1074            bail!("`dependency.{name}` was not found in `workspace.dependencies`");
    1075        };
    1076        let mut dep = dep.clone();
    1077        if let manifest::TomlDependency::Detailed(detailed) = &mut dep {
    1078            if detailed.base.is_none() {
    1079                // If this is a path dependency without a base, then update the path to be relative
    1080                // to the workspace root instead.
    1081                if let Some(rel_path) = &detailed.path {
    1082                    detailed.path = Some(resolve_relative_path(
    1083                        name,
    1084                        self.ws_root(),
    1085                        package_root,
    1086                        rel_path,
    1087                    )?);
    1088                }
    1089            }
    1090        }
    1091        Ok(dep)
    1092    }
    1093
    1094    /// Gets the field `workspace.lints`.
    1095    pub fn lints(&self) -> CargoResult<manifest::TomlLints> {
    1096        let Some(val) = &self.lints else {
    1097            bail!("`workspace.lints` was not defined");
    1098        };
    1099        Ok(val.clone())
    1100    }
    1101
    1102    /// Gets the field `workspace.package.license-file`.
    1103    fn license_file(&self, package_root: &Path) -> CargoResult<String> {
    1104        let Some(license_file) = self.package.as_ref().and_then(|p| p.license_file.as_ref()) else {
    1105            bail!("`workspace.package.license-file` was not defined");
    1106        };
    1107        resolve_relative_path("license-file", &self._ws_root, package_root, license_file)
    1108    }
    1109
    1110    /// Gets the field `workspace.package.readme`.
    1111    fn readme(&self, package_root: &Path) -> CargoResult<manifest::StringOrBool> {
    1112        let Some(readme) = normalize_package_readme(
    1113            self._ws_root.as_path(),
    1114            self.package.as_ref().and_then(|p| p.readme.as_ref()),
    1115        ) else {
    1116            bail!("`workspace.package.readme` was not defined");
    1117        };
    1118        resolve_relative_path("readme", &self._ws_root, package_root, &readme)
    1119            .map(manifest::StringOrBool::String)
    1120    }
    1121
    1122    fn ws_root(&self) -> &PathBuf {
    1123        &self._ws_root
    1124    }
    1125}
    1126
    1127fn field_inherit_with<'a, T>(
    1128    field: manifest::InheritableField<T>,
    1129    label: &str,
    1130    get_ws_inheritable: impl FnOnce() -> CargoResult<T>,
    1131) -> CargoResult<T> {
    1132    match field {
    1133        manifest::InheritableField::Value(value) => Ok(value),
    1134        manifest::InheritableField::Inherit(_) => get_ws_inheritable().with_context(|| {
    1135            format!(
    1136                "error inheriting `{label}` from workspace root manifest's `workspace.package.{label}`",
    1137            )
    1138        }),
    1139    }
    1140}
    1141
    1142fn lints_inherit_with(
    1143    lints: manifest::InheritableLints,
    1144    get_ws_inheritable: impl FnOnce() -> CargoResult<manifest::TomlLints>,
    1145) -> CargoResult<manifest::TomlLints> {
    1146    if lints.workspace {
    1147        if !lints.lints.is_empty() {
    1148            anyhow::bail!(
    1149                "cannot override `workspace.lints` in `lints`, either remove the overrides or `lints.workspace = true` and manually specify the lints"
    1150            );
    1151        }
    1152        get_ws_inheritable().with_context(
    1153            || "error inheriting `lints` from workspace root manifest's `workspace.lints`",
    1154        )
    1155    } else {
    1156        Ok(lints.lints)
    1157    }
    1158}
    1159
    1160fn dependency_inherit_with<'a>(
    1161    dependency: manifest::InheritableDependency,
    1162    name: &str,
    1163    inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
    1164    package_root: &Path,
    1165    edition: Edition,
    1166    warnings: &mut Vec<String>,
    1167) -> CargoResult<manifest::TomlDependency> {
    1168    match dependency {
    1169        manifest::InheritableDependency::Value(value) => Ok(value),
    1170        manifest::InheritableDependency::Inherit(w) => {
    1171            inner_dependency_inherit_with(w, name, inherit, package_root, edition, warnings).with_context(|| {
    1172                format!(
    1173                    "error inheriting `{name}` from workspace root manifest's `workspace.dependencies.{name}`",
    1174                )
    1175            })
    1176        }
    1177    }
    1178}
    1179
    1180fn inner_dependency_inherit_with<'a>(
    1181    pkg_dep: manifest::TomlInheritedDependency,
    1182    name: &str,
    1183    inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
    1184    package_root: &Path,
    1185    edition: Edition,
    1186    warnings: &mut Vec<String>,
    1187) -> CargoResult<manifest::TomlDependency> {
    1188    let ws_dep = inherit()?.get_dependency(name, package_root)?;
    1189    let mut merged_dep = match ws_dep {
    1190        manifest::TomlDependency::Simple(ws_version) => manifest::TomlDetailedDependency {
    1191            version: Some(ws_version),
    1192            ..Default::default()
    1193        },
    1194        manifest::TomlDependency::Detailed(ws_dep) => ws_dep.clone(),
    1195    };
    1196    let manifest::TomlInheritedDependency {
    1197        workspace: _,
    1198
    1199        features,
    1200        optional,
    1201        default_features,
    1202        default_features2,
    1203        public,
    1204
    1205        _unused_keys: _,
    1206    } = &pkg_dep;
    1207    let default_features = default_features.or(*default_features2);
    1208
    1209    match (default_features, merged_dep.default_features()) {
    1210        // member: default-features = true and
    1211        // workspace: default-features = false should turn on
    1212        // default-features
    1213        (Some(true), Some(false)) => {
    1214            merged_dep.default_features = Some(true);
    1215        }
    1216        // member: default-features = false and
    1217        // workspace: default-features = true should ignore member
    1218        // default-features
    1219        (Some(false), Some(true)) => {
    1220            deprecated_ws_default_features(name, Some(true), edition, warnings)?;
    1221        }
    1222        // member: default-features = false and
    1223        // workspace: dep = "1.0" should ignore member default-features
    1224        (Some(false), None) => {
    1225            deprecated_ws_default_features(name, None, edition, warnings)?;
    1226        }
    1227        _ => {}
    1228    }
    1229    merged_dep.features = match (merged_dep.features.clone(), features.clone()) {
    1230        (Some(dep_feat), Some(inherit_feat)) => Some(
    1231            dep_feat
    1232                .into_iter()
    1233                .chain(inherit_feat)
    1234                .collect::<Vec<String>>(),
    1235        ),
    1236        (Some(dep_fet), None) => Some(dep_fet),
    1237        (None, Some(inherit_feat)) => Some(inherit_feat),
    1238        (None, None) => None,
    1239    };
    1240    merged_dep.optional = *optional;
    1241    merged_dep.public = *public;
    1242    Ok(manifest::TomlDependency::Detailed(merged_dep))
    1243}
    1244
    1245fn deprecated_ws_default_features(
    1246    label: &str,
    1247    ws_def_feat: Option<bool>,
    1248    edition: Edition,
    1249    warnings: &mut Vec<String>,
    1250) -> CargoResult<()> {
    1251    let ws_def_feat = match ws_def_feat {
    1252        Some(true) => "true",
    1253        Some(false) => "false",
    1254        None => "not specified",
    1255    };
    1256    if Edition::Edition2024 <= edition {
    1257        anyhow::bail!("`default-features = false` cannot override workspace's `default-features`");
    1258    } else {
    1259        warnings.push(format!(
    1260            "`default-features` is ignored for {label}, since `default-features` was \
    1261                {ws_def_feat} for `workspace.dependencies.{label}`, \
    1262                this could become a hard error in the future"
    1263        ));
    1264    }
    1265    Ok(())
    1266}
    1267
    1268#[tracing::instrument(skip_all)]
    1269pub fn to_real_manifest(
    1270    contents: Option<String>,
    1271    document: Option<toml::Spanned<toml::de::DeTable<'static>>>,
    1272    original_toml: manifest::TomlManifest,
    1273    normalized_toml: manifest::TomlManifest,
    1274    features: Features,
    1275    workspace_config: WorkspaceConfig,
    1276    source_id: SourceId,
    1277    manifest_file: &Path,
    1278    is_embedded: bool,
    1279    gctx: &GlobalContext,
    1280    warnings: &mut Vec<String>,
    1281    _errors: &mut Vec<String>,
    1282) -> CargoResult<Manifest> {
    1283    let package_root = manifest_file.parent().unwrap();
    1284    if !package_root.is_dir() {
    1285        bail!(
    1286            "package root '{}' is not a directory",
    1287            package_root.display()
    1288        );
    1289    };
    1290
    1291    let normalized_package = normalized_toml
    1292        .package()
    1293        .expect("previously verified to have a `[package]`");
    1294    let package_name = normalized_package
    1295        .normalized_name()
    1296        .expect("previously normalized");
    1297    if package_name.contains(':') {
    1298        features.require(Feature::open_namespaces())?;
    1299    }
    1300    let rust_version = normalized_package
    1301        .normalized_rust_version()
    1302        .expect("previously normalized")
    1303        .cloned();
    1304
    1305    let edition = if let Some(edition) = normalized_package
    1306        .normalized_edition()
    1307        .expect("previously normalized")
    1308    {
    1309        let edition: Edition = edition
    1310            .parse()
    1311            .context("failed to parse the `edition` key")?;
    1312        if let Some(pkg_msrv) = &rust_version {
    1313            if let Some(edition_msrv) = edition.first_version() {
    1314                let edition_msrv = RustVersion::try_from(edition_msrv).unwrap();
    1315                if !edition_msrv.is_compatible_with(pkg_msrv.as_partial()) {
    1316                    bail!(
    1317                        "rust-version {} is incompatible with the version ({}) required by \
    1318                            the specified edition ({})",
    1319                        pkg_msrv,
    1320                        edition_msrv,
    1321                        edition,
    1322                    )
    1323                }
    1324            }
    1325        }
    1326        edition
    1327    } else {
    1328        let msrv_edition = if let Some(pkg_msrv) = &rust_version {
    1329            Edition::ALL
    1330                .iter()
    1331                .filter(|e| {
    1332                    e.first_version()
    1333                        .map(|e| {
    1334                            let e = RustVersion::try_from(e).unwrap();
    1335                            e.is_compatible_with(pkg_msrv.as_partial())
    1336                        })
    1337                        .unwrap_or_default()
    1338                })
    1339                .max()
    1340                .copied()
    1341        } else {
    1342            None
    1343        }
    1344        .unwrap_or_default();
    1345        let default_edition = Edition::default();
    1346        let latest_edition = Edition::LATEST_STABLE;
    1347
    1348        // We're trying to help the user who might assume they are using a new edition,
    1349        // so if they can't use a new edition, don't bother to tell them to set it.
    1350        // This also avoids having to worry about whether `package.edition` is compatible with
    1351        // their MSRV.
    1352        if msrv_edition != default_edition || rust_version.is_none() {
    1353            let tip = if msrv_edition == latest_edition || rust_version.is_none() {
    1354                format!(" while the latest is {latest_edition}")
    1355            } else {
    1356                format!(" while {msrv_edition} is compatible with `rust-version`")
    1357            };
    1358            warnings.push(format!(
    1359                "no edition set: defaulting to the {default_edition} edition{tip}",
    1360            ));
    1361        }
    1362        default_edition
    1363    };
    1364    if !edition.is_stable() {
    1365        features.require(Feature::unstable_editions())?;
    1366    }
    1367
    1368    if original_toml.project.is_some() {
    1369        if Edition::Edition2024 <= edition {
    1370            anyhow::bail!(
    1371                "`[project]` is not supported as of the 2024 Edition, please use `[package]`"
    1372            );
    1373        } else {
    1374            warnings.push(format!("`[project]` is deprecated in favor of `[package]`"));
    1375        }
    1376    }
    1377
    1378    if normalized_package.metabuild.is_some() {
    1379        features.require(Feature::metabuild())?;
    1380    }
    1381
    1382    if is_embedded {
    1383        let manifest::TomlManifest {
    1384            cargo_features: _,
    1385            package: _,
    1386            project: _,
    1387            badges: _,
    1388            features: _,
    1389            lib,
    1390            bin,
    1391            example,
    1392            test,
    1393            bench,
    1394            dependencies: _,
    1395            dev_dependencies: _,
    1396            dev_dependencies2: _,
    1397            build_dependencies,
    1398            build_dependencies2,
    1399            target: _,
    1400            lints: _,
    1401            hints: _,
    1402            workspace,
    1403            profile: _,
    1404            patch: _,
    1405            replace: _,
    1406            _unused_keys: _,
    1407        } = &original_toml;
    1408        let mut invalid_fields = vec![
    1409            ("`workspace`", workspace.is_some()),
    1410            ("`lib`", lib.is_some()),
    1411            ("`bin`", bin.is_some()),
    1412            ("`example`", example.is_some()),
    1413            ("`test`", test.is_some()),
    1414            ("`bench`", bench.is_some()),
    1415            ("`build-dependencies`", build_dependencies.is_some()),
    1416            ("`build_dependencies`", build_dependencies2.is_some()),
    1417        ];
    1418        if let Some(package) = original_toml.package() {
    1419            let manifest::TomlPackage {
    1420                edition: _,
    1421                rust_version: _,
    1422                name: _,
    1423                version: _,
    1424                authors: _,
    1425                build,
    1426                metabuild,
    1427                default_target: _,
    1428                forced_target: _,
    1429                links,
    1430                exclude: _,
    1431                include: _,
    1432                publish: _,
    1433                workspace,
    1434                im_a_teapot: _,
    1435                autolib,
    1436                autobins,
    1437                autoexamples,
    1438                autotests,
    1439                autobenches,
    1440                default_run,
    1441                description: _,
    1442                homepage: _,
    1443                documentation: _,
    1444                readme: _,
    1445                keywords: _,
    1446                categories: _,
    1447                license: _,
    1448                license_file: _,
    1449                repository: _,
    1450                resolver: _,
    1451                metadata: _,
    1452                _invalid_cargo_features: _,
    1453            } = package.as_ref();
    1454            invalid_fields.extend([
    1455                ("`package.workspace`", workspace.is_some()),
    1456                ("`package.build`", build.is_some()),
    1457                ("`package.metabuild`", metabuild.is_some()),
    1458                ("`package.links`", links.is_some()),
    1459                ("`package.autolib`", autolib.is_some()),
    1460                ("`package.autobins`", autobins.is_some()),
    1461                ("`package.autoexamples`", autoexamples.is_some()),
    1462                ("`package.autotests`", autotests.is_some()),
    1463                ("`package.autobenches`", autobenches.is_some()),
    1464                ("`package.default-run`", default_run.is_some()),
    1465            ]);
    1466        }
    1467        let invalid_fields = invalid_fields
    1468            .into_iter()
    1469            .filter_map(|(name, invalid)| invalid.then_some(name))
    1470            .collect::<Vec<_>>();
    1471        if !invalid_fields.is_empty() {
    1472            let fields = invalid_fields.join(", ");
    1473            let are = if invalid_fields.len() == 1 {
    1474                "is"
    1475            } else {
    1476                "are"
    1477            };
    1478            anyhow::bail!("{fields} {are} not allowed in embedded manifests")
    1479        }
    1480    }
    1481
    1482    let resolve_behavior = match (
    1483        normalized_package.resolver.as_ref(),
    1484        normalized_toml
    1485            .workspace
    1486            .as_ref()
    1487            .and_then(|ws| ws.resolver.as_ref()),
    1488    ) {
    1489        (None, None) => None,
    1490        (Some(s), None) | (None, Some(s)) => Some(ResolveBehavior::from_manifest(s)?),
    1491        (Some(_), Some(_)) => {
    1492            bail!("cannot specify `resolver` field in both `[workspace]` and `[package]`")
    1493        }
    1494    };
    1495
    1496    // If we have no lib at all, use the inferred lib, if available.
    1497    // If we have a lib with a path, we're done.
    1498    // If we have a lib with no path, use the inferred lib or else the package name.
    1499    let targets = to_targets(
    1500        &features,
    1501        &original_toml,
    1502        &normalized_toml,
    1503        package_root,
    1504        edition,
    1505        &normalized_package.metabuild,
    1506        warnings,
    1507    )?;
    1508
    1509    if targets.iter().all(|t| t.is_custom_build()) {
    1510        bail!(
    1511            "no targets specified in the manifest\n\
    1512                 either src/lib.rs, src/main.rs, a [lib] section, or \
    1513                 [[bin]] section must be present"
    1514        )
    1515    }
    1516
    1517    if let Err(conflict_targets) = unique_build_targets(&targets, package_root) {
    1518        conflict_targets
    1519            .iter()
    1520            .for_each(|(target_path, conflicts)| {
    1521                warnings.push(format!(
    1522                    "file `{}` found to be present in multiple \
    1523                 build targets:\n{}",
    1524                    target_path.display(),
    1525                    conflicts
    1526                        .iter()
    1527                        .map(|t| format!("  * `{}` target `{}`", t.kind().description(), t.name(),))
    1528                        .join("\n")
    1529                ));
    1530            })
    1531    }
    1532
    1533    if let Some(links) = &normalized_package.links {
    1534        if !targets.iter().any(|t| t.is_custom_build()) {
    1535            bail!(
    1536                "package specifies that it links to `{links}` but does not have a custom build script"
    1537            )
    1538        }
    1539    }
    1540
    1541    validate_dependencies(original_toml.dependencies.as_ref(), None, None, warnings)?;
    1542    validate_dependencies(
    1543        original_toml.dev_dependencies(),
    1544        None,
    1545        Some(DepKind::Development),
    1546        warnings,
    1547    )?;
    1548    validate_dependencies(
    1549        original_toml.build_dependencies(),
    1550        None,
    1551        Some(DepKind::Build),
    1552        warnings,
    1553    )?;
    1554    for (name, platform) in original_toml.target.iter().flatten() {
    1555        let platform_kind: Platform = name.parse()?;
    1556        platform_kind.check_cfg_attributes(warnings);
    1557        platform_kind.check_cfg_keywords(warnings, manifest_file);
    1558        let platform_kind = Some(platform_kind);
    1559        validate_dependencies(
    1560            platform.dependencies.as_ref(),
    1561            platform_kind.as_ref(),
    1562            None,
    1563            warnings,
    1564        )?;
    1565        validate_dependencies(
    1566            platform.build_dependencies(),
    1567            platform_kind.as_ref(),
    1568            Some(DepKind::Build),
    1569            warnings,
    1570        )?;
    1571        validate_dependencies(
    1572            platform.dev_dependencies(),
    1573            platform_kind.as_ref(),
    1574            Some(DepKind::Development),
    1575            warnings,
    1576        )?;
    1577    }
    1578
    1579    // Collect the dependencies.
    1580    let mut deps = Vec::new();
    1581    let mut manifest_ctx = ManifestContext {
    1582        deps: &mut deps,
    1583        source_id,
    1584        gctx,
    1585        warnings,
    1586        platform: None,
    1587        file: manifest_file,
    1588    };
    1589    gather_dependencies(
    1590        &mut manifest_ctx,
    1591        normalized_toml.dependencies.as_ref(),
    1592        None,
    1593    )?;
    1594    gather_dependencies(
    1595        &mut manifest_ctx,
    1596        normalized_toml.dev_dependencies(),
    1597        Some(DepKind::Development),
    1598    )?;
    1599    gather_dependencies(
    1600        &mut manifest_ctx,
    1601        normalized_toml.build_dependencies(),
    1602        Some(DepKind::Build),
    1603    )?;
    1604    for (name, platform) in normalized_toml.target.iter().flatten() {
    1605        manifest_ctx.platform = Some(name.parse()?);
    1606        gather_dependencies(&mut manifest_ctx, platform.dependencies.as_ref(), None)?;
    1607        gather_dependencies(
    1608            &mut manifest_ctx,
    1609            platform.build_dependencies(),
    1610            Some(DepKind::Build),
    1611        )?;
    1612        gather_dependencies(
    1613            &mut manifest_ctx,
    1614            platform.dev_dependencies(),
    1615            Some(DepKind::Development),
    1616        )?;
    1617    }
    1618    let replace = replace(&normalized_toml, &mut manifest_ctx)?;
    1619    let patch = patch(&normalized_toml, &mut manifest_ctx)?;
    1620
    1621    {
    1622        let mut names_sources = BTreeMap::new();
    1623        for dep in &deps {
    1624            let name = dep.name_in_toml();
    1625            let prev = names_sources.insert(name, dep.source_id());
    1626            if prev.is_some() && prev != Some(dep.source_id()) {
    1627                bail!(
    1628                    "Dependency '{}' has different source paths depending on the build \
    1629                         target. Each dependency must have a single canonical source path \
    1630                         irrespective of build target.",
    1631                    name
    1632                );
    1633            }
    1634        }
    1635    }
    1636
    1637    verify_lints(
    1638        normalized_toml
    1639            .normalized_lints()
    1640            .expect("previously normalized"),
    1641        gctx,
    1642        warnings,
    1643    )?;
    1644    let default = manifest::TomlLints::default();
    1645    let rustflags = lints_to_rustflags(
    1646        normalized_toml
    1647            .normalized_lints()
    1648            .expect("previously normalized")
    1649            .unwrap_or(&default),
    1650    )?;
    1651
    1652    let hints = normalized_toml.hints.clone();
    1653
    1654    let metadata = ManifestMetadata {
    1655        description: normalized_package
    1656            .normalized_description()
    1657            .expect("previously normalized")
    1658            .cloned(),
    1659        homepage: normalized_package
    1660            .normalized_homepage()
    1661            .expect("previously normalized")
    1662            .cloned(),
    1663        documentation: normalized_package
    1664            .normalized_documentation()
    1665            .expect("previously normalized")
    1666            .cloned(),
    1667        readme: normalized_package
    1668            .normalized_readme()
    1669            .expect("previously normalized")
    1670            .cloned(),
    1671        authors: normalized_package
    1672            .normalized_authors()
    1673            .expect("previously normalized")
    1674            .cloned()
    1675            .unwrap_or_default(),
    1676        license: normalized_package
    1677            .normalized_license()
    1678            .expect("previously normalized")
    1679            .cloned(),
    1680        license_file: normalized_package
    1681            .normalized_license_file()
    1682            .expect("previously normalized")
    1683            .cloned(),
    1684        repository: normalized_package
    1685            .normalized_repository()
    1686            .expect("previously normalized")
    1687            .cloned(),
    1688        keywords: normalized_package
    1689            .normalized_keywords()
    1690            .expect("previously normalized")
    1691            .cloned()
    1692            .unwrap_or_default(),
    1693        categories: normalized_package
    1694            .normalized_categories()
    1695            .expect("previously normalized")
    1696            .cloned()
    1697            .unwrap_or_default(),
    1698        badges: normalized_toml.badges.clone().unwrap_or_default(),
    1699        links: normalized_package.links.clone(),
    1700        rust_version: rust_version.clone(),
    1701    };
    1702
    1703    if let Some(profiles) = &normalized_toml.profile {
    1704        let cli_unstable = gctx.cli_unstable();
    1705        validate_profiles(profiles, cli_unstable, &features, warnings)?;
    1706    }
    1707
    1708    let version = normalized_package
    1709        .normalized_version()
    1710        .expect("previously normalized");
    1711    let publish = match normalized_package
    1712        .normalized_publish()
    1713        .expect("previously normalized")
    1714    {
    1715        Some(manifest::VecStringOrBool::VecString(vecstring)) => Some(vecstring.clone()),
    1716        Some(manifest::VecStringOrBool::Bool(false)) => Some(vec![]),
    1717        Some(manifest::VecStringOrBool::Bool(true)) => None,
    1718        None => version.is_none().then_some(vec![]),
    1719    };
    1720
    1721    if version.is_none() && publish != Some(vec![]) {
    1722        bail!("`package.publish` requires `package.version` be specified");
    1723    }
    1724
    1725    let pkgid = PackageId::new(
    1726        package_name.as_str().into(),
    1727        version
    1728            .cloned()
    1729            .unwrap_or_else(|| semver::Version::new(0, 0, 0)),
    1730        source_id,
    1731    );
    1732    let summary = {
    1733        let summary = Summary::new(
    1734            pkgid,
    1735            deps,
    1736            &normalized_toml
    1737                .features
    1738                .as_ref()
    1739                .unwrap_or(&Default::default())
    1740                .iter()
    1741                .map(|(k, v)| {
    1742                    (
    1743                        k.to_string().into(),
    1744                        v.iter().map(InternedString::from).collect(),
    1745                    )
    1746                })
    1747                .collect(),
    1748            normalized_package.links.as_deref(),
    1749            rust_version.clone(),
    1750        );
    1751        // edition2024 stops exposing implicit features, which will strip weak optional dependencies from `dependencies`,
    1752        // need to check whether `dep_name` is stripped as unused dependency
    1753        if let Err(ref err) = summary {
    1754            if let Some(missing_dep) = err.downcast_ref::<MissingDependencyError>() {
    1755                missing_dep_diagnostic(
    1756                    missing_dep,
    1757                    &original_toml,
    1758                    document.as_ref(),
    1759                    contents.as_deref(),
    1760                    manifest_file,
    1761                    gctx,
    1762                )?;
    1763            }
    1764        }
    1765        summary?
    1766    };
    1767
    1768    if summary.features().contains_key("default-features") {
    1769        warnings.push(
    1770            "`[features]` defines a feature named `default-features`
    1771note: only a feature named `default` will be enabled by default"
    1772                .to_string(),
    1773        )
    1774    }
    1775
    1776    if let Some(run) = &normalized_package.default_run {
    1777        if !targets
    1778            .iter()
    1779            .filter(|t| t.is_bin())
    1780            .any(|t| t.name() == run)
    1781        {
    1782            let suggestion = util::closest_msg(
    1783                run,
    1784                targets.iter().filter(|t| t.is_bin()),
    1785                |t| t.name(),
    1786                "target",
    1787            );
    1788            bail!("default-run target `{}` not found{}", run, suggestion);
    1789        }
    1790    }
    1791
    1792    let default_kind = normalized_package
    1793        .default_target
    1794        .as_ref()
    1795        .map(|t| CompileTarget::new(&*t))
    1796        .transpose()?
    1797        .map(CompileKind::Target);
    1798    let forced_kind = normalized_package
    1799        .forced_target
    1800        .as_ref()
    1801        .map(|t| CompileTarget::new(&*t))
    1802        .transpose()?
    1803        .map(CompileKind::Target);
    1804    let include = normalized_package
    1805        .normalized_include()
    1806        .expect("previously normalized")
    1807        .cloned()
    1808        .unwrap_or_default();
    1809    let exclude = normalized_package
    1810        .normalized_exclude()
    1811        .expect("previously normalized")
    1812        .cloned()
    1813        .unwrap_or_default();
    1814    let links = normalized_package.links.clone();
    1815    let custom_metadata = normalized_package.metadata.clone();
    1816    let im_a_teapot = normalized_package.im_a_teapot;
    1817    let default_run = normalized_package.default_run.clone();
    1818    let metabuild = normalized_package.metabuild.clone().map(|sov| sov.0);
    1819    let manifest = Manifest::new(
    1820        contents.map(Rc::new),
    1821        document.map(Rc::new),
    1822        Some(Rc::new(original_toml)),
    1823        Rc::new(normalized_toml),
    1824        summary,
    1825        default_kind,
    1826        forced_kind,
    1827        targets,
    1828        exclude,
    1829        include,
    1830        links,
    1831        metadata,
    1832        custom_metadata,
    1833        publish,
    1834        replace,
    1835        patch,
    1836        workspace_config,
    1837        features,
    1838        edition,
    1839        rust_version,
    1840        im_a_teapot,
    1841        default_run,
    1842        metabuild,
    1843        resolve_behavior,
    1844        rustflags,
    1845        hints,
    1846        is_embedded,
    1847    );
    1848    if manifest
    1849        .normalized_toml()
    1850        .package()
    1851        .unwrap()
    1852        .license_file
    1853        .is_some()
    1854        && manifest
    1855            .normalized_toml()
    1856            .package()
    1857            .unwrap()
    1858            .license
    1859            .is_some()
    1860    {
    1861        warnings.push(
    1862            "only one of `license` or `license-file` is necessary\n\
    1863                 `license` should be used if the package license can be expressed \
    1864                 with a standard SPDX expression.\n\
    1865                 `license-file` should be used if the package uses a non-standard license.\n\
    1866                 See https://doc.rust-lang.org/cargo/reference/manifest.html#the-license-and-license-file-fields \
    1867                 for more information."
    1868                .to_owned(),
    1869        );
    1870    }
    1871    if let Some(original_toml) = manifest.original_toml() {
    1872        warn_on_unused(&original_toml._unused_keys, warnings);
    1873    }
    1874
    1875    manifest.feature_gate()?;
    1876
    1877    Ok(manifest)
    1878}
    1879
    1880fn missing_dep_diagnostic(
    1881    missing_dep: &MissingDependencyError,
    1882    orig_toml: &TomlManifest,
    1883    document: Option<&toml::Spanned<toml::de::DeTable<'static>>>,
    1884    contents: Option<&str>,
    1885    manifest_file: &Path,
    1886    gctx: &GlobalContext,
    1887) -> CargoResult<()> {
    1888    let dep_name = missing_dep.dep_name;
    1889    let manifest_path = rel_cwd_manifest_path(manifest_file, gctx);
    1890
    1891    let title = format!(
    1892        "feature `{}` includes `{}`, but `{}` is not a dependency",
    1893        missing_dep.feature, missing_dep.feature_value, &dep_name
    1894    );
    1895    let help = format!("enable the dependency with `dep:{dep_name}`");
    1896    let info_label = format!(
    1897        "`{}` is an unused optional dependency since no feature enables it",
    1898        &dep_name
    1899    );
    1900    let group = Group::with_title(Level::ERROR.primary_title(&title));
    1901    let group =
    1902        if let Some(contents) = contents
    1903            && let Some(document) = document
    1904        {
    1905            let feature_span =
    1906                get_key_value_span(&document, &["features", missing_dep.feature.as_str()]).unwrap();
    1907
    1908            let snippet = Snippet::source(contents)
    1909                .path(manifest_path)
    1910                .annotation(AnnotationKind::Primary.span(feature_span.value));
    1911
    1912            if missing_dep.weak_optional {
    1913                let mut orig_deps = vec![
    1914                    (
    1915                        orig_toml.dependencies.as_ref(),
    1916                        vec![DepKind::Normal.kind_table()],
    1917                    ),
    1918                    (
    1919                        orig_toml.build_dependencies.as_ref(),
    1920                        vec![DepKind::Build.kind_table()],
    1921                    ),
    1922                ];
    1923                for (name, platform) in orig_toml.target.iter().flatten() {
    1924                    orig_deps.push((
    1925                        platform.dependencies.as_ref(),
    1926                        vec!["target", name, DepKind::Normal.kind_table()],
    1927                    ));
    1928                    orig_deps.push((
    1929                        platform.build_dependencies.as_ref(),
    1930                        vec!["target", name, DepKind::Normal.kind_table()],
    1931                    ));
    1932                }
    1933
    1934                if let Some((_, toml_path)) = orig_deps.iter().find(|(deps, _)| {
    1935                    if let Some(deps) = deps {
    1936                        deps.keys().any(|p| *p.as_str() == *dep_name)
    1937                    } else {
    1938                        false
    1939                    }
    1940                }) {
    1941                    let toml_path = toml_path
    1942                        .iter()
    1943                        .map(|s| *s)
    1944                        .chain(std::iter::once(dep_name.as_str()))
    1945                        .collect::<Vec<_>>();
    1946                    let dep_span = get_key_value_span(&document, &toml_path).unwrap();
    1947
    1948                    group
    1949                        .element(snippet.annotation(
    1950                            AnnotationKind::Context.span(dep_span.key).label(info_label),
    1951                        ))
    1952                        .element(Level::HELP.message(help))
    1953                } else {
    1954                    group.element(snippet)
    1955                }
    1956            } else {
    1957                group.element(snippet)
    1958            }
    1959        } else {
    1960            group
    1961        };
    1962
    1963    if let Err(err) = gctx.shell().print_report(&[group], true) {
    1964        return Err(err.into());
    1965    }
    1966    Err(AlreadyPrintedError::new(anyhow!("").into()).into())
    1967}
    1968
    1969fn to_virtual_manifest(
    1970    contents: Option<String>,
    1971    document: Option<toml::Spanned<toml::de::DeTable<'static>>>,
    1972    original_toml: manifest::TomlManifest,
    1973    normalized_toml: manifest::TomlManifest,
    1974    features: Features,
    1975    workspace_config: WorkspaceConfig,
    1976    source_id: SourceId,
    1977    manifest_file: &Path,
    1978    gctx: &GlobalContext,
    1979    warnings: &mut Vec<String>,
    1980    _errors: &mut Vec<String>,
    1981) -> CargoResult<VirtualManifest> {
    1982    let mut deps = Vec::new();
    1983    let (replace, patch) = {
    1984        let mut manifest_ctx = ManifestContext {
    1985            deps: &mut deps,
    1986            source_id,
    1987            gctx,
    1988            warnings,
    1989            platform: None,
    1990            file: manifest_file,
    1991        };
    1992        (
    1993            replace(&normalized_toml, &mut manifest_ctx)?,
    1994            patch(&normalized_toml, &mut manifest_ctx)?,
    1995        )
    1996    };
    1997    if let Some(profiles) = &normalized_toml.profile {
    1998        validate_profiles(profiles, gctx.cli_unstable(), &features, warnings)?;
    1999    }
    2000    let resolve_behavior = normalized_toml
    2001        .workspace
    2002        .as_ref()
    2003        .and_then(|ws| ws.resolver.as_deref())
    2004        .map(|r| ResolveBehavior::from_manifest(r))
    2005        .transpose()?;
    2006    if let WorkspaceConfig::Member { .. } = &workspace_config {
    2007        bail!("virtual manifests must be configured with [workspace]");
    2008    }
    2009    let manifest = VirtualManifest::new(
    2010        contents.map(Rc::new),
    2011        document.map(Rc::new),
    2012        Some(Rc::new(original_toml)),
    2013        Rc::new(normalized_toml),
    2014        replace,
    2015        patch,
    2016        workspace_config,
    2017        features,
    2018        resolve_behavior,
    2019    );
    2020
    2021    if let Some(original_toml) = manifest.original_toml() {
    2022        warn_on_unused(&original_toml._unused_keys, warnings);
    2023    }
    2024
    2025    Ok(manifest)
    2026}
    2027
    2028#[tracing::instrument(skip_all)]
    2029fn validate_dependencies(
    2030    original_deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
    2031    platform: Option<&Platform>,
    2032    kind: Option<DepKind>,
    2033    warnings: &mut Vec<String>,
    2034) -> CargoResult<()> {
    2035    let Some(dependencies) = original_deps else {
    2036        return Ok(());
    2037    };
    2038
    2039    for (name_in_toml, v) in dependencies.iter() {
    2040        let kind_name = match kind {
    2041            Some(k) => k.kind_table(),
    2042            None => "dependencies",
    2043        };
    2044        let table_in_toml = if let Some(platform) = platform {
    2045            format!("target.{platform}.{kind_name}")
    2046        } else {
    2047            kind_name.to_string()
    2048        };
    2049        unused_dep_keys(name_in_toml, &table_in_toml, v.unused_keys(), warnings);
    2050    }
    2051    Ok(())
    2052}
    2053
    2054struct ManifestContext<'a, 'b> {
    2055    deps: &'a mut Vec<Dependency>,
    2056    source_id: SourceId,
    2057    gctx: &'b GlobalContext,
    2058    warnings: &'a mut Vec<String>,
    2059    platform: Option<Platform>,
    2060    file: &'a Path,
    2061}
    2062
    2063#[tracing::instrument(skip_all)]
    2064fn gather_dependencies(
    2065    manifest_ctx: &mut ManifestContext<'_, '_>,
    2066    normalized_deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
    2067    kind: Option<DepKind>,
    2068) -> CargoResult<()> {
    2069    let Some(dependencies) = normalized_deps else {
    2070        return Ok(());
    2071    };
    2072
    2073    for (n, v) in dependencies.iter() {
    2074        let resolved = v.normalized().expect("previously normalized");
    2075        let dep = dep_to_dependency(&resolved, n, manifest_ctx, kind)?;
    2076        manifest_ctx.deps.push(dep);
    2077    }
    2078    Ok(())
    2079}
    2080
    2081fn replace(
    2082    me: &manifest::TomlManifest,
    2083    manifest_ctx: &mut ManifestContext<'_, '_>,
    2084) -> CargoResult<Vec<(PackageIdSpec, Dependency)>> {
    2085    if me.patch.is_some() && me.replace.is_some() {
    2086        bail!("cannot specify both [replace] and [patch]");
    2087    }
    2088    let mut replace = Vec::new();
    2089    for (spec, replacement) in me.replace.iter().flatten() {
    2090        let mut spec = PackageIdSpec::parse(spec).with_context(|| {
    2091            format!(
    2092                "replacements must specify a valid semver \
    2093                     version to replace, but `{}` does not",
    2094                spec
    2095            )
    2096        })?;
    2097        if spec.url().is_none() {
    2098            spec.set_url(CRATES_IO_INDEX.parse().unwrap());
    2099        }
    2100
    2101        if replacement.is_version_specified() {
    2102            bail!(
    2103                "replacements cannot specify a version \
    2104                     requirement, but found one for `{}`",
    2105                spec
    2106            );
    2107        }
    2108
    2109        let mut dep = dep_to_dependency(replacement, spec.name(), manifest_ctx, None)?;
    2110        let version = spec.version().ok_or_else(|| {
    2111            anyhow!(
    2112                "replacements must specify a version \
    2113                     to replace, but `{}` does not",
    2114                spec
    2115            )
    2116        })?;
    2117        unused_dep_keys(
    2118            dep.name_in_toml().as_str(),
    2119            "replace",
    2120            replacement.unused_keys(),
    2121            &mut manifest_ctx.warnings,
    2122        );
    2123        dep.set_version_req(OptVersionReq::exact(&version));
    2124        replace.push((spec, dep));
    2125    }
    2126    Ok(replace)
    2127}
    2128
    2129fn patch(
    2130    me: &TomlManifest,
    2131    manifest_ctx: &mut ManifestContext<'_, '_>,
    2132) -> CargoResult<HashMap<Url, Vec<Patch>>> {
    2133    let mut patch = HashMap::new();
    2134    for (toml_url, deps) in me.patch.iter().flatten() {
    2135        let url = match &toml_url[..] {
    2136            CRATES_IO_REGISTRY => CRATES_IO_INDEX.parse().unwrap(),
    2137            _ => manifest_ctx
    2138                .gctx
    2139                .get_registry_index(toml_url)
    2140                .or_else(|_| toml_url.into_url())
    2141                .with_context(|| {
    2142                    format!(
    2143                        "[patch] entry `{}` should be a URL or registry name{}",
    2144                        toml_url,
    2145                        if toml_url == "crates" {
    2146                            "\nFor crates.io, use [patch.crates-io] (with a dash)"
    2147                        } else {
    2148                            ""
    2149                        }
    2150                    )
    2151                })?,
    2152        };
    2153        patch.insert(
    2154            url,
    2155            deps.iter()
    2156                .map(|(name, dep)| {
    2157                    unused_dep_keys(
    2158                        name,
    2159                        &format!("patch.{toml_url}",),
    2160                        dep.unused_keys(),
    2161                        &mut manifest_ctx.warnings,
    2162                    );
    2163
    2164                    let dep = dep_to_dependency(dep, name, manifest_ctx, None)?;
    2165                    let loc = PatchLocation::Manifest(manifest_ctx.file.to_path_buf());
    2166                    Ok(Patch { dep, loc })
    2167                })
    2168                .collect::<CargoResult<Vec<_>>>()?,
    2169        );
    2170    }
    2171    Ok(patch)
    2172}
    2173
    2174/// Transforms a `patch` entry from Cargo config to a [`Dependency`].
    2175pub(crate) fn config_patch_to_dependency<P: ResolveToPath + Clone>(
    2176    config_patch: &manifest::TomlDependency<P>,
    2177    name: &str,
    2178    source_id: SourceId,
    2179    gctx: &GlobalContext,
    2180    warnings: &mut Vec<String>,
    2181) -> CargoResult<Dependency> {
    2182    let manifest_ctx = &mut ManifestContext {
    2183        deps: &mut Vec::new(),
    2184        source_id,
    2185        gctx,
    2186        warnings,
    2187        platform: None,
    2188        // config path doesn't have manifest file path, and doesn't use it.
    2189        file: Path::new("unused"),
    2190    };
    2191    dep_to_dependency(config_patch, name, manifest_ctx, None)
    2192}
    2193
    2194fn dep_to_dependency<P: ResolveToPath + Clone>(
    2195    orig: &manifest::TomlDependency<P>,
    2196    name_in_toml: &str,
    2197    manifest_ctx: &mut ManifestContext<'_, '_>,
    2198    kind: Option<DepKind>,
    2199) -> CargoResult<Dependency> {
    2200    let orig = match orig {
    2201        manifest::TomlDependency::Simple(version) => &manifest::TomlDetailedDependency::<P> {
    2202            version: Some(version.clone()),
    2203            ..Default::default()
    2204        },
    2205        manifest::TomlDependency::Detailed(details) => details,
    2206    };
    2207
    2208    if orig.version.is_none() && orig.path.is_none() && orig.git.is_none() {
    2209        anyhow::bail!(
    2210            "dependency ({name_in_toml}) specified without \
    2211                 providing a local path, Git repository, version, or \
    2212                 workspace dependency to use"
    2213        );
    2214    }
    2215
    2216    if let Some(version) = &orig.version {
    2217        if version.contains('+') {
    2218            manifest_ctx.warnings.push(format!(
    2219                "version requirement `{}` for dependency `{}` \
    2220                     includes semver metadata which will be ignored, removing the \
    2221                     metadata is recommended to avoid confusion",
    2222                version, name_in_toml
    2223            ));
    2224        }
    2225    }
    2226
    2227    if orig.git.is_none() {
    2228        let git_only_keys = [
    2229            (&orig.branch, "branch"),
    2230            (&orig.tag, "tag"),
    2231            (&orig.rev, "rev"),
    2232        ];
    2233
    2234        for &(key, key_name) in &git_only_keys {
    2235            if key.is_some() {
    2236                bail!(
    2237                    "key `{}` is ignored for dependency ({}).",
    2238                    key_name,
    2239                    name_in_toml
    2240                );
    2241            }
    2242        }
    2243    }
    2244
    2245    // Early detection of potentially misused feature syntax
    2246    // instead of generating a "feature not found" error.
    2247    if let Some(features) = &orig.features {
    2248        for feature in features {
    2249            if feature.contains('/') {
    2250                bail!(
    2251                    "feature `{}` in dependency `{}` is not allowed to contain slashes\n\
    2252                         If you want to enable features of a transitive dependency, \
    2253                         the direct dependency needs to re-export those features from \
    2254                         the `[features]` table.",
    2255                    feature,
    2256                    name_in_toml
    2257                );
    2258            }
    2259            if feature.starts_with("dep:") {
    2260                bail!(
    2261                    "feature `{}` in dependency `{}` is not allowed to use explicit \
    2262                        `dep:` syntax\n\
    2263                         If you want to enable an optional dependency, specify the name \
    2264                         of the optional dependency without the `dep:` prefix, or specify \
    2265                         a feature from the dependency's `[features]` table that enables \
    2266                         the optional dependency.",
    2267                    feature,
    2268                    name_in_toml
    2269                );
    2270            }
    2271        }
    2272    }
    2273
    2274    let new_source_id = to_dependency_source_id(orig, name_in_toml, manifest_ctx)?;
    2275
    2276    let (pkg_name, explicit_name_in_toml) = match orig.package {
    2277        Some(ref s) => (&s[..], Some(name_in_toml)),
    2278        None => (name_in_toml, None),
    2279    };
    2280
    2281    let version = orig.version.as_deref();
    2282    let mut dep = Dependency::parse(pkg_name, version, new_source_id)?;
    2283    dep.set_features(orig.features.iter().flatten())
    2284        .set_default_features(orig.default_features().unwrap_or(true))
    2285        .set_optional(orig.optional.unwrap_or(false))
    2286        .set_platform(manifest_ctx.platform.clone());
    2287    if let Some(registry) = &orig.registry {
    2288        let registry_id = SourceId::alt_registry(manifest_ctx.gctx, registry)?;
    2289        dep.set_registry_id(registry_id);
    2290    }
    2291    if let Some(registry_index) = &orig.registry_index {
    2292        let url = registry_index.into_url()?;
    2293        let registry_id = SourceId::for_registry(&url)?;
    2294        dep.set_registry_id(registry_id);
    2295    }
    2296
    2297    if let Some(kind) = kind {
    2298        dep.set_kind(kind);
    2299    }
    2300    if let Some(name_in_toml) = explicit_name_in_toml {
    2301        dep.set_explicit_name_in_toml(name_in_toml);
    2302    }
    2303
    2304    if let Some(p) = orig.public {
    2305        dep.set_public(p);
    2306    }
    2307
    2308    if let (Some(artifact), is_lib, target) = (
    2309        orig.artifact.as_ref(),
    2310        orig.lib.unwrap_or(false),
    2311        orig.target.as_deref(),
    2312    ) {
    2313        if manifest_ctx.gctx.cli_unstable().bindeps {
    2314            let artifact = Artifact::parse(&artifact.0, is_lib, target)?;
    2315            if dep.kind() != DepKind::Build
    2316                && artifact.target() == Some(ArtifactTarget::BuildDependencyAssumeTarget)
    2317            {
    2318                bail!(
    2319                    r#"`target = "target"` in normal- or dev-dependencies has no effect ({})"#,
    2320                    name_in_toml
    2321                );
    2322            }
    2323            dep.set_artifact(artifact)
    2324        } else {
    2325            bail!("`artifact = …` requires `-Z bindeps` ({})", name_in_toml);
    2326        }
    2327    } else if orig.lib.is_some() || orig.target.is_some() {
    2328        for (is_set, specifier) in [
    2329            (orig.lib.is_some(), "lib"),
    2330            (orig.target.is_some(), "target"),
    2331        ] {
    2332            if !is_set {
    2333                continue;
    2334            }
    2335            bail!(
    2336                "'{}' specifier cannot be used without an 'artifact = …' value ({})",
    2337                specifier,
    2338                name_in_toml
    2339            )
    2340        }
    2341    }
    2342    Ok(dep)
    2343}
    2344
    2345fn to_dependency_source_id<P: ResolveToPath + Clone>(
    2346    orig: &manifest::TomlDetailedDependency<P>,
    2347    name_in_toml: &str,
    2348    manifest_ctx: &mut ManifestContext<'_, '_>,
    2349) -> CargoResult<SourceId> {
    2350    match (
    2351        orig.git.as_ref(),
    2352        orig.path.as_ref(),
    2353        orig.registry.as_deref(),
    2354        orig.registry_index.as_ref(),
    2355    ) {
    2356        (Some(_git), _, Some(_registry), _) | (Some(_git), _, _, Some(_registry)) => bail!(
    2357            "dependency ({name_in_toml}) specification is ambiguous. \
    2358                 Only one of `git` or `registry` is allowed.",
    2359        ),
    2360        (_, _, Some(_registry), Some(_registry_index)) => bail!(
    2361            "dependency ({name_in_toml}) specification is ambiguous. \
    2362                 Only one of `registry` or `registry-index` is allowed.",
    2363        ),
    2364        (Some(_git), Some(_path), None, None) => {
    2365            bail!(
    2366                "dependency ({name_in_toml}) specification is ambiguous. \
    2367                     Only one of `git` or `path` is allowed.",
    2368            );
    2369        }
    2370        (Some(git), None, None, None) => {
    2371            let n_details = [&orig.branch, &orig.tag, &orig.rev]
    2372                .iter()
    2373                .filter(|d| d.is_some())
    2374                .count();
    2375
    2376            if n_details > 1 {
    2377                bail!(
    2378                    "dependency ({name_in_toml}) specification is ambiguous. \
    2379                         Only one of `branch`, `tag` or `rev` is allowed.",
    2380                );
    2381            }
    2382
    2383            let reference = orig
    2384                .branch
    2385                .clone()
    2386                .map(GitReference::Branch)
    2387                .or_else(|| orig.tag.clone().map(GitReference::Tag))
    2388                .or_else(|| orig.rev.clone().map(GitReference::Rev))
    2389                .unwrap_or(GitReference::DefaultBranch);
    2390            let loc = git.into_url()?;
    2391
    2392            if let Some(fragment) = loc.fragment() {
    2393                let msg = format!(
    2394                    "URL fragment `#{fragment}` in git URL is ignored for dependency ({name_in_toml}). \
    2395                        If you were trying to specify a specific git revision, \
    2396                        use `rev = \"{fragment}\"` in the dependency declaration.",
    2397                );
    2398                manifest_ctx.warnings.push(msg);
    2399            }
    2400
    2401            SourceId::for_git(&loc, reference)
    2402        }
    2403        (None, Some(path), _, _) => {
    2404            let path = path.resolve(manifest_ctx.gctx);
    2405            // If the source ID for the package we're parsing is a path
    2406            // source, then we normalize the path here to get rid of
    2407            // components like `..`.
    2408            //
    2409            // The purpose of this is to get a canonical ID for the package
    2410            // that we're depending on to ensure that builds of this package
    2411            // always end up hashing to the same value no matter where it's
    2412            // built from.
    2413            if manifest_ctx.source_id.is_path() {
    2414                let path = manifest_ctx.file.parent().unwrap().join(path);
    2415                let path = paths::normalize_path(&path);
    2416                SourceId::for_path(&path)
    2417            } else {
    2418                Ok(manifest_ctx.source_id)
    2419            }
    2420        }
    2421        (None, None, Some(registry), None) => SourceId::alt_registry(manifest_ctx.gctx, registry),
    2422        (None, None, None, Some(registry_index)) => {
    2423            let url = registry_index.into_url()?;
    2424            SourceId::for_registry(&url)
    2425        }
    2426        (None, None, None, None) => SourceId::crates_io(manifest_ctx.gctx),
    2427    }
    2428}
    2429
    2430pub(crate) fn lookup_path_base<'a>(
    2431    base: &PathBaseName,
    2432    gctx: &GlobalContext,
    2433    workspace_root: &dyn Fn() -> CargoResult<&'a Path>,
    2434    features: &Features,
    2435) -> CargoResult<PathBuf> {
    2436    features.require(Feature::path_bases())?;
    2437
    2438    // HACK: The `base` string is user controlled, but building the path is safe from injection
    2439    // attacks since the `PathBaseName` type restricts the characters that can be used to exclude `.`
    2440    let base_key = format!("path-bases.{base}");
    2441
    2442    // Look up the relevant base in the Config and use that as the root.
    2443    if let Some(path_bases) = gctx.get::<Option<ConfigRelativePath>>(&base_key)? {
    2444        Ok(path_bases.resolve_path(gctx))
    2445    } else {
    2446        // Otherwise, check the built-in bases.
    2447        match base.as_str() {
    2448            "workspace" => Ok(workspace_root()?.to_path_buf()),
    2449            _ => bail!(
    2450                "path base `{base}` is undefined. \
    2451            You must add an entry for `{base}` in the Cargo configuration [path-bases] table."
    2452            ),
    2453        }
    2454    }
    2455}
    2456
    2457pub trait ResolveToPath {
    2458    fn resolve(&self, gctx: &GlobalContext) -> PathBuf;
    2459}
    2460
    2461impl ResolveToPath for String {
    2462    fn resolve(&self, _: &GlobalContext) -> PathBuf {
    2463        self.into()
    2464    }
    2465}
    2466
    2467impl ResolveToPath for ConfigRelativePath {
    2468    fn resolve(&self, gctx: &GlobalContext) -> PathBuf {
    2469        self.resolve_path(gctx)
    2470    }
    2471}
    2472
    2473/// Checks a list of build targets, and ensures the target names are unique within a vector.
    2474/// If not, the name of the offending build target is returned.
    2475#[tracing::instrument(skip_all)]
    2476fn unique_build_targets(
    2477    targets: &[Target],
    2478    package_root: &Path,
    2479) -> Result<(), HashMap<PathBuf, Vec<Target>>> {
    2480    let mut source_targets = HashMap::<_, Vec<_>>::new();
    2481    for target in targets {
    2482        if let TargetSourcePath::Path(path) = target.src_path() {
    2483            let full = package_root.join(path);
    2484            source_targets.entry(full).or_default().push(target.clone());
    2485        }
    2486    }
    2487
    2488    let conflict_targets = source_targets
    2489        .into_iter()
    2490        .filter(|(_, targets)| targets.len() > 1)
    2491        .collect::<HashMap<_, _>>();
    2492
    2493    if !conflict_targets.is_empty() {
    2494        return Err(conflict_targets);
    2495    }
    2496
    2497    Ok(())
    2498}
    2499
    2500/// Checks syntax validity and unstable feature gate for each profile.
    2501///
    2502/// It's a bit unfortunate both `-Z` flags and `cargo-features` are required,
    2503/// because profiles can now be set in either `Cargo.toml` or `config.toml`.
    2504fn validate_profiles(
    2505    profiles: &manifest::TomlProfiles,
    2506    cli_unstable: &CliUnstable,
    2507    features: &Features,
    2508    warnings: &mut Vec<String>,
    2509) -> CargoResult<()> {
    2510    for (name, profile) in &profiles.0 {
    2511        validate_profile(profile, name, cli_unstable, features, warnings)?;
    2512    }
    2513    Ok(())
    2514}
    2515
    2516/// Checks syntax validity and unstable feature gate for a given profile.
    2517pub fn validate_profile(
    2518    root: &manifest::TomlProfile,
    2519    name: &str,
    2520    cli_unstable: &CliUnstable,
    2521    features: &Features,
    2522    warnings: &mut Vec<String>,
    2523) -> CargoResult<()> {
    2524    validate_profile_layer(root, cli_unstable, features)?;
    2525    if let Some(ref profile) = root.build_override {
    2526        validate_profile_override(profile, "build-override")?;
    2527        validate_profile_layer(profile, cli_unstable, features)?;
    2528    }
    2529    if let Some(ref packages) = root.package {
    2530        for profile in packages.values() {
    2531            validate_profile_override(profile, "package")?;
    2532            validate_profile_layer(profile, cli_unstable, features)?;
    2533        }
    2534    }
    2535
    2536    if let Some(dir_name) = &root.dir_name {
    2537        // This is disabled for now, as we would like to stabilize named
    2538        // profiles without this, and then decide in the future if it is
    2539        // needed. This helps simplify the UI a little.
    2540        bail!(
    2541            "dir-name=\"{}\" in profile `{}` is not currently allowed, \
    2542                 directory names are tied to the profile name for custom profiles",
    2543            dir_name,
    2544            name
    2545        );
    2546    }
    2547
    2548    // `inherits` validation
    2549    if matches!(root.inherits.as_deref(), Some("debug")) {
    2550        bail!(
    2551            "profile.{}.inherits=\"debug\" should be profile.{}.inherits=\"dev\"",
    2552            name,
    2553            name
    2554        );
    2555    }
    2556
    2557    match name {
    2558        "doc" => {
    2559            warnings.push("profile `doc` is deprecated and has no effect".to_string());
    2560        }
    2561        "test" | "bench" => {
    2562            if root.panic.is_some() {
    2563                warnings.push(format!("`panic` setting is ignored for `{}` profile", name))
    2564            }
    2565        }
    2566        _ => {}
    2567    }
    2568
    2569    if let Some(panic) = &root.panic {
    2570        if panic != "unwind" && panic != "abort" && panic != "immediate-abort" {
    2571            bail!(
    2572                "`panic` setting of `{}` is not a valid setting, \
    2573                     must be `unwind`, `abort`, or `immediate-abort`.",
    2574                panic
    2575            );
    2576        }
    2577    }
    2578
    2579    if let Some(manifest::StringOrBool::String(arg)) = &root.lto {
    2580        if arg == "true" || arg == "false" {
    2581            bail!(
    2582                "`lto` setting of string `\"{arg}\"` for `{name}` profile is not \
    2583                     a valid setting, must be a boolean (`true`/`false`) or a string \
    2584                    (`\"thin\"`/`\"fat\"`/`\"off\"`) or omitted.",
    2585            );
    2586        }
    2587    }
    2588
    2589    Ok(())
    2590}
    2591
    2592/// Validates a profile.
    2593///
    2594/// This is a shallow check, which is reused for the profile itself and any overrides.
    2595fn validate_profile_layer(
    2596    profile: &manifest::TomlProfile,
    2597    cli_unstable: &CliUnstable,
    2598    features: &Features,
    2599) -> CargoResult<()> {
    2600    if profile.codegen_backend.is_some() {
    2601        match (
    2602            features.require(Feature::codegen_backend()),
    2603            cli_unstable.codegen_backend,
    2604        ) {
    2605            (Err(e), false) => return Err(e),
    2606            _ => {}
    2607        }
    2608    }
    2609    if profile.rustflags.is_some() {
    2610        match (
    2611            features.require(Feature::profile_rustflags()),
    2612            cli_unstable.profile_rustflags,
    2613        ) {
    2614            (Err(e), false) => return Err(e),
    2615            _ => {}
    2616        }
    2617    }
    2618    if profile.trim_paths.is_some() {
    2619        match (
    2620            features.require(Feature::trim_paths()),
    2621            cli_unstable.trim_paths,
    2622        ) {
    2623            (Err(e), false) => return Err(e),
    2624            _ => {}
    2625        }
    2626    }
    2627    if profile.panic.as_deref() == Some("immediate-abort") {
    2628        match (
    2629            features.require(Feature::panic_immediate_abort()),
    2630            cli_unstable.panic_immediate_abort,
    2631        ) {
    2632            (Err(e), false) => return Err(e),
    2633            _ => {}
    2634        }
    2635    }
    2636    Ok(())
    2637}
    2638
    2639/// Validation that is specific to an override.
    2640fn validate_profile_override(profile: &manifest::TomlProfile, which: &str) -> CargoResult<()> {
    2641    if profile.package.is_some() {
    2642        bail!("package-specific profiles cannot be nested");
    2643    }
    2644    if profile.build_override.is_some() {
    2645        bail!("build-override profiles cannot be nested");
    2646    }
    2647    if profile.panic.is_some() {
    2648        bail!("`panic` may not be specified in a `{}` profile", which)
    2649    }
    2650    if profile.lto.is_some() {
    2651        bail!("`lto` may not be specified in a `{}` profile", which)
    2652    }
    2653    if profile.rpath.is_some() {
    2654        bail!("`rpath` may not be specified in a `{}` profile", which)
    2655    }
    2656    Ok(())
    2657}
    2658
    2659fn verify_lints(
    2660    lints: Option<&manifest::TomlLints>,
    2661    gctx: &GlobalContext,
    2662    warnings: &mut Vec<String>,
    2663) -> CargoResult<()> {
    2664    let Some(lints) = lints else {
    2665        return Ok(());
    2666    };
    2667
    2668    for (tool, lints) in lints {
    2669        let supported = ["cargo", "clippy", "rust", "rustdoc"];
    2670        if !supported.contains(&tool.as_str()) {
    2671            let message = format!(
    2672                "unrecognized lint tool `lints.{tool}`, specifying unrecognized tools may break in the future.
    2673supported tools: {}",
    2674                supported.join(", "),
    2675            );
    2676            warnings.push(message);
    2677            continue;
    2678        }
    2679        if tool == "cargo" && !gctx.cli_unstable().cargo_lints {
    2680            warn_for_cargo_lint_feature(gctx, warnings);
    2681        }
    2682        for (name, config) in lints {
    2683            if let Some((prefix, suffix)) = name.split_once("::") {
    2684                if tool == prefix {
    2685                    anyhow::bail!(
    2686                        "`lints.{tool}.{name}` is not valid lint name; try `lints.{prefix}.{suffix}`"
    2687                    )
    2688                } else if tool == "rust" && supported.contains(&prefix) {
    2689                    anyhow::bail!(
    2690                        "`lints.{tool}.{name}` is not valid lint name; try `lints.{prefix}.{suffix}`"
    2691                    )
    2692                } else {
    2693                    anyhow::bail!("`lints.{tool}.{name}` is not a valid lint name")
    2694                }
    2695            } else if let Some(config) = config.config() {
    2696                for config_name in config.keys() {
    2697                    // manually report unused manifest key warning since we collect all the "extra"
    2698                    // keys and values inside the config table
    2699                    //
    2700                    // except for `rust.unexpected_cfgs.check-cfg` which is used by rustc/rustdoc
    2701                    if !(tool == "rust" && name == "unexpected_cfgs" && config_name == "check-cfg")
    2702                    {
    2703                        let message =
    2704                            format!("unused manifest key: `lints.{tool}.{name}.{config_name}`");
    2705                        warnings.push(message);
    2706                    }
    2707                }
    2708            }
    2709        }
    2710    }
    2711
    2712    Ok(())
    2713}
    2714
    2715fn warn_for_cargo_lint_feature(gctx: &GlobalContext, warnings: &mut Vec<String>) {
    2716    use std::fmt::Write as _;
    2717
    2718    let key_name = "lints.cargo";
    2719    let feature_name = "cargo-lints";
    2720
    2721    let mut message = String::new();
    2722
    2723    let _ = write!(
    2724        message,
    2725        "unused manifest key `{key_name}` (may be supported in a future version)"
    2726    );
    2727    if gctx.nightly_features_allowed {
    2728        let _ = write!(
    2729            message,
    2730            "
    2731
    2732consider passing `-Z{feature_name}` to enable this feature."
    2733        );
    2734    } else {
    2735        let _ = write!(
    2736            message,
    2737            "
    2738
    2739this Cargo does not support nightly features, but if you
    2740switch to nightly channel you can pass
    2741`-Z{feature_name}` to enable this feature.",
    2742        );
    2743    }
    2744    warnings.push(message);
    2745}
    2746
    2747fn lints_to_rustflags(lints: &manifest::TomlLints) -> CargoResult<Vec<String>> {
    2748    let mut rustflags = lints
    2749        .iter()
    2750        // We don't want to pass any of the `cargo` lints to `rustc`
    2751        .filter(|(tool, _)| tool != &"cargo")
    2752        .flat_map(|(tool, lints)| {
    2753            lints.iter().map(move |(name, config)| {
    2754                let flag = match config.level() {
    2755                    manifest::TomlLintLevel::Forbid => "--forbid",
    2756                    manifest::TomlLintLevel::Deny => "--deny",
    2757                    manifest::TomlLintLevel::Warn => "--warn",
    2758                    manifest::TomlLintLevel::Allow => "--allow",
    2759                };
    2760
    2761                let option = if tool == "rust" {
    2762                    format!("{flag}={name}")
    2763                } else {
    2764                    format!("{flag}={tool}::{name}")
    2765                };
    2766                (
    2767                    config.priority(),
    2768                    // Since the most common group will be `all`, put it last so people are more
    2769                    // likely to notice that they need to use `priority`.
    2770                    std::cmp::Reverse(name),
    2771                    option,
    2772                )
    2773            })
    2774        })
    2775        .collect::<Vec<_>>();
    2776    rustflags.sort();
    2777
    2778    let mut rustflags: Vec<_> = rustflags.into_iter().map(|(_, _, option)| option).collect();
    2779
    2780    // Also include the custom arguments specified in `[lints.rust.unexpected_cfgs.check_cfg]`
    2781    if let Some(rust_lints) = lints.get("rust") {
    2782        if let Some(unexpected_cfgs) = rust_lints.get("unexpected_cfgs") {
    2783            if let Some(config) = unexpected_cfgs.config() {
    2784                if let Some(check_cfg) = config.get("check-cfg") {
    2785                    if let Ok(check_cfgs) = toml::Value::try_into::<Vec<String>>(check_cfg.clone())
    2786                    {
    2787                        for check_cfg in check_cfgs {
    2788                            rustflags.push("--check-cfg".to_string());
    2789                            rustflags.push(check_cfg);
    2790                        }
    2791                    // error about `check-cfg` not being a list-of-string
    2792                    } else {
    2793                        bail!("`lints.rust.unexpected_cfgs.check-cfg` must be a list of string");
    2794                    }
    2795                }
    2796            }
    2797        }
    2798    }
    2799
    2800    Ok(rustflags)
    2801}
    2802
    2803fn emit_frontmatter_diagnostic(
    2804    e: crate::util::frontmatter::FrontmatterError,
    2805    contents: &str,
    2806    manifest_file: &Path,
    2807    gctx: &GlobalContext,
    2808) -> anyhow::Error {
    2809    let primary_span = e.primary_span();
    2810
    2811    // Get the path to the manifest, relative to the cwd
    2812    let manifest_path = diff_paths(manifest_file, gctx.cwd())
    2813        .unwrap_or_else(|| manifest_file.to_path_buf())
    2814        .display()
    2815        .to_string();
    2816    let group = Group::with_title(Level::ERROR.primary_title(e.message())).element(
    2817        Snippet::source(contents)
    2818            .path(manifest_path)
    2819            .annotation(AnnotationKind::Primary.span(primary_span))
    2820            .annotations(
    2821                e.visible_spans()
    2822                    .iter()
    2823                    .map(|s| AnnotationKind::Visible.span(s.clone())),
    2824            ),
    2825    );
    2826
    2827    if let Err(err) = gctx.shell().print_report(&[group], true) {
    2828        return err.into();
    2829    }
    2830    return AlreadyPrintedError::new(e.into()).into();
    2831}
    2832
    2833fn emit_toml_diagnostic(
    2834    e: toml::de::Error,
    2835    contents: &str,
    2836    manifest_file: &Path,
    2837    gctx: &GlobalContext,
    2838) -> anyhow::Error {
    2839    let Some(span) = e.span() else {
    2840        return e.into();
    2841    };
    2842
    2843    // Get the path to the manifest, relative to the cwd
    2844    let manifest_path = diff_paths(manifest_file, gctx.cwd())
    2845        .unwrap_or_else(|| manifest_file.to_path_buf())
    2846        .display()
    2847        .to_string();
    2848    let group = Group::with_title(Level::ERROR.primary_title(e.message())).element(
    2849        Snippet::source(contents)
    2850            .path(manifest_path)
    2851            .annotation(AnnotationKind::Primary.span(span)),
    2852    );
    2853
    2854    if let Err(err) = gctx.shell().print_report(&[group], true) {
    2855        return err.into();
    2856    }
    2857    return AlreadyPrintedError::new(e.into()).into();
    2858}
    2859
    2860/// Warn about paths that have been deprecated and may conflict.
    2861fn deprecated_underscore<T>(
    2862    old: &Option<T>,
    2863    new: &Option<T>,
    2864    new_path: &str,
    2865    name: &str,
    2866    kind: &str,
    2867    edition: Edition,
    2868    warnings: &mut Vec<String>,
    2869) -> CargoResult<()> {
    2870    let old_path = new_path.replace("-", "_");
    2871    if old.is_some() && Edition::Edition2024 <= edition {
    2872        anyhow::bail!(
    2873            "`{old_path}` is unsupported as of the 2024 edition; instead use `{new_path}`\n(in the `{name}` {kind})"
    2874        );
    2875    } else if old.is_some() && new.is_some() {
    2876        warnings.push(format!(
    2877            "`{old_path}` is redundant with `{new_path}`, preferring `{new_path}` in the `{name}` {kind}"
    2878        ))
    2879    } else if old.is_some() {
    2880        warnings.push(format!(
    2881            "`{old_path}` is deprecated in favor of `{new_path}` and will not work in the 2024 edition\n(in the `{name}` {kind})"
    2882        ))
    2883    }
    2884    Ok(())
    2885}
    2886
    2887fn warn_on_unused(unused: &BTreeSet<String>, warnings: &mut Vec<String>) {
    2888    use std::fmt::Write as _;
    2889
    2890    for key in unused {
    2891        let mut message = format!("unused manifest key: {}", key);
    2892        if TOP_LEVEL_CONFIG_KEYS.iter().any(|c| c == key) {
    2893            write!(
    2894                &mut message,
    2895                "\nhelp: {key} is a valid .cargo/config.toml key"
    2896            )
    2897            .unwrap();
    2898        }
    2899        warnings.push(message);
    2900    }
    2901}
    2902
    2903fn unused_dep_keys(
    2904    dep_name: &str,
    2905    kind: &str,
    2906    unused_keys: Vec<String>,
    2907    warnings: &mut Vec<String>,
    2908) {
    2909    for unused in unused_keys {
    2910        let key = format!("unused manifest key: {kind}.{dep_name}.{unused}");
    2911        warnings.push(key);
    2912    }
    2913}
    2914
    2915/// Make the [`Package`] self-contained so its ready for packaging
    2916pub fn prepare_for_publish(
    2917    me: &Package,
    2918    ws: &Workspace<'_>,
    2919    packaged_files: Option<&[PathBuf]>,
    2920) -> CargoResult<Package> {
    2921    let contents = me.manifest().contents();
    2922    let document = me.manifest().document();
    2923    let original_toml = prepare_toml_for_publish(
    2924        me.manifest().normalized_toml(),
    2925        ws,
    2926        me.root(),
    2927        packaged_files,
    2928    )?;
    2929    let normalized_toml = original_toml.clone();
    2930    let features = me.manifest().unstable_features().clone();
    2931    let workspace_config = me.manifest().workspace_config().clone();
    2932    let source_id = me.package_id().source_id();
    2933    let mut warnings = Default::default();
    2934    let mut errors = Default::default();
    2935    let gctx = ws.gctx();
    2936    let manifest = to_real_manifest(
    2937        contents.map(|c| c.to_owned()),
    2938        document.cloned(),
    2939        original_toml,
    2940        normalized_toml,
    2941        features,
    2942        workspace_config,
    2943        source_id,
    2944        me.manifest_path(),
    2945        me.manifest().is_embedded(),
    2946        gctx,
    2947        &mut warnings,
    2948        &mut errors,
    2949    )?;
    2950    let new_pkg = Package::new(manifest, me.manifest_path());
    2951    Ok(new_pkg)
    2952}
    2953
    2954/// Prepares the manifest for publishing.
    2955// - Path and git components of dependency specifications are removed.
    2956// - License path is updated to point within the package.
    2957fn prepare_toml_for_publish(
    2958    me: &manifest::TomlManifest,
    2959    ws: &Workspace<'_>,
    2960    package_root: &Path,
    2961    packaged_files: Option<&[PathBuf]>,
    2962) -> CargoResult<manifest::TomlManifest> {
    2963    let gctx = ws.gctx();
    2964
    2965    if me
    2966        .cargo_features
    2967        .iter()
    2968        .flat_map(|f| f.iter())
    2969        .any(|f| f == "open-namespaces")
    2970    {
    2971        anyhow::bail!("cannot publish with `open-namespaces`")
    2972    }
    2973
    2974    let mut package = me.package().unwrap().clone();
    2975    package.workspace = None;
    2976    // Validates if build script file is included in package. If not, warn and ignore.
    2977    if let Some(custom_build_scripts) = package.normalized_build().expect("previously normalized") {
    2978        let mut included_scripts = Vec::new();
    2979        for script in custom_build_scripts {
    2980            let path = Path::new(script).to_path_buf();
    2981            let included = packaged_files.map(|i| i.contains(&path)).unwrap_or(true);
    2982            if included {
    2983                let path = path
    2984                    .into_os_string()
    2985                    .into_string()
    2986                    .map_err(|_err| anyhow::format_err!("non-UTF8 `package.build`"))?;
    2987                let path = normalize_path_string_sep(path);
    2988                included_scripts.push(path);
    2989            } else {
    2990                ws.gctx().shell().warn(format!(
    2991                    "ignoring `package.build` entry `{}` as it is not included in the published package",
    2992                    path.display()
    2993                ))?;
    2994            }
    2995        }
    2996
    2997        package.build = Some(match included_scripts.len() {
    2998            0 => TomlPackageBuild::Auto(false),
    2999            1 => TomlPackageBuild::SingleScript(included_scripts[0].clone()),
    3000            _ => TomlPackageBuild::MultipleScript(included_scripts),
    3001        });
    3002    }
    3003    let current_resolver = package
    3004        .resolver
    3005        .as_ref()
    3006        .map(|r| ResolveBehavior::from_manifest(r))
    3007        .unwrap_or_else(|| {
    3008            package
    3009                .edition
    3010                .as_ref()
    3011                .and_then(|e| e.as_value())
    3012                .map(|e| Edition::from_str(e))
    3013                .unwrap_or(Ok(Edition::Edition2015))
    3014                .map(|e| e.default_resolve_behavior())
    3015        })?;
    3016    if ws.resolve_behavior() != current_resolver {
    3017        // This ensures the published crate if built as a root (e.g. `cargo install`) will
    3018        // use the same resolver behavior it was tested with in the workspace.
    3019        // To avoid forcing a higher MSRV we don't explicitly set this if it would implicitly
    3020        // result in the same thing.
    3021        package.resolver = Some(ws.resolve_behavior().to_manifest());
    3022    }
    3023    if let Some(license_file) = &package.license_file {
    3024        let license_file = license_file
    3025            .as_value()
    3026            .context("license file should have been resolved before `prepare_for_publish()`")?;
    3027        let license_path = Path::new(&license_file);
    3028        let abs_license_path = paths::normalize_path(&package_root.join(license_path));
    3029        if let Ok(license_file) = abs_license_path.strip_prefix(package_root) {
    3030            package.license_file = Some(manifest::InheritableField::Value(
    3031                normalize_path_string_sep(
    3032                    license_file
    3033                        .to_str()
    3034                        .ok_or_else(|| anyhow::format_err!("non-UTF8 `package.license-file`"))?
    3035                        .to_owned(),
    3036                ),
    3037            ));
    3038        } else {
    3039            // This path points outside of the package root. `cargo package`
    3040            // will copy it into the root, so adjust the path to this location.
    3041            package.license_file = Some(manifest::InheritableField::Value(
    3042                license_path
    3043                    .file_name()
    3044                    .unwrap()
    3045                    .to_str()
    3046                    .unwrap()
    3047                    .to_string(),
    3048            ));
    3049        }
    3050    }
    3051
    3052    if let Some(readme) = &package.readme {
    3053        let readme = readme
    3054            .as_value()
    3055            .context("readme should have been resolved before `prepare_for_publish()`")?;
    3056        match readme {
    3057            manifest::StringOrBool::String(readme) => {
    3058                let readme_path = Path::new(&readme);
    3059                let abs_readme_path = paths::normalize_path(&package_root.join(readme_path));
    3060                if let Ok(readme_path) = abs_readme_path.strip_prefix(package_root) {
    3061                    package.readme = Some(manifest::InheritableField::Value(StringOrBool::String(
    3062                        normalize_path_string_sep(
    3063                            readme_path
    3064                                .to_str()
    3065                                .ok_or_else(|| {
    3066                                    anyhow::format_err!("non-UTF8 `package.license-file`")
    3067                                })?
    3068                                .to_owned(),
    3069                        ),
    3070                    )));
    3071                } else {
    3072                    // This path points outside of the package root. `cargo package`
    3073                    // will copy it into the root, so adjust the path to this location.
    3074                    package.readme = Some(manifest::InheritableField::Value(
    3075                        manifest::StringOrBool::String(
    3076                            readme_path
    3077                                .file_name()
    3078                                .unwrap()
    3079                                .to_str()
    3080                                .unwrap()
    3081                                .to_string(),
    3082                        ),
    3083                    ));
    3084                }
    3085            }
    3086            manifest::StringOrBool::Bool(_) => {}
    3087        }
    3088    }
    3089
    3090    let lib = if let Some(target) = &me.lib {
    3091        prepare_target_for_publish(target, packaged_files, "library", ws.gctx())?
    3092    } else {
    3093        None
    3094    };
    3095    let bin = prepare_targets_for_publish(me.bin.as_ref(), packaged_files, "binary", ws.gctx())?;
    3096    let example =
    3097        prepare_targets_for_publish(me.example.as_ref(), packaged_files, "example", ws.gctx())?;
    3098    let test = prepare_targets_for_publish(me.test.as_ref(), packaged_files, "test", ws.gctx())?;
    3099    let bench =
    3100        prepare_targets_for_publish(me.bench.as_ref(), packaged_files, "benchmark", ws.gctx())?;
    3101
    3102    let all = |_d: &manifest::TomlDependency| true;
    3103    let mut manifest = manifest::TomlManifest {
    3104        cargo_features: me.cargo_features.clone(),
    3105        package: Some(package),
    3106        project: None,
    3107        badges: me.badges.clone(),
    3108        features: me.features.clone(),
    3109        lib,
    3110        bin,
    3111        example,
    3112        test,
    3113        bench,
    3114        dependencies: map_deps(gctx, me.dependencies.as_ref(), all)?,
    3115        dev_dependencies: map_deps(
    3116            gctx,
    3117            me.dev_dependencies(),
    3118            manifest::TomlDependency::is_version_specified,
    3119        )?,
    3120        dev_dependencies2: None,
    3121        build_dependencies: map_deps(gctx, me.build_dependencies(), all)?,
    3122        build_dependencies2: None,
    3123        target: match me.target.as_ref().map(|target_map| {
    3124            target_map
    3125                .iter()
    3126                .map(|(k, v)| {
    3127                    Ok((
    3128                        k.clone(),
    3129                        manifest::TomlPlatform {
    3130                            dependencies: map_deps(gctx, v.dependencies.as_ref(), all)?,
    3131                            dev_dependencies: map_deps(
    3132                                gctx,
    3133                                v.dev_dependencies(),
    3134                                manifest::TomlDependency::is_version_specified,
    3135                            )?,
    3136                            dev_dependencies2: None,
    3137                            build_dependencies: map_deps(gctx, v.build_dependencies(), all)?,
    3138                            build_dependencies2: None,
    3139                        },
    3140                    ))
    3141                })
    3142                .collect()
    3143        }) {
    3144            Some(Ok(v)) => Some(v),
    3145            Some(Err(e)) => return Err(e),
    3146            None => None,
    3147        },
    3148        lints: me.lints.clone(),
    3149        hints: me.hints.clone(),
    3150        workspace: None,
    3151        profile: me.profile.clone(),
    3152        patch: None,
    3153        replace: None,
    3154        _unused_keys: Default::default(),
    3155    };
    3156    strip_features(&mut manifest);
    3157    return Ok(manifest);
    3158
    3159    fn strip_features(manifest: &mut TomlManifest) {
    3160        fn insert_dep_name(
    3161            dep_name_set: &mut BTreeSet<manifest::PackageName>,
    3162            deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
    3163        ) {
    3164            let Some(deps) = deps else {
    3165                return;
    3166            };
    3167            deps.iter().for_each(|(k, _v)| {
    3168                dep_name_set.insert(k.clone());
    3169            });
    3170        }
    3171        let mut dep_name_set = BTreeSet::new();
    3172        insert_dep_name(&mut dep_name_set, manifest.dependencies.as_ref());
    3173        insert_dep_name(&mut dep_name_set, manifest.dev_dependencies());
    3174        insert_dep_name(&mut dep_name_set, manifest.build_dependencies());
    3175        if let Some(target_map) = manifest.target.as_ref() {
    3176            target_map.iter().for_each(|(_k, v)| {
    3177                insert_dep_name(&mut dep_name_set, v.dependencies.as_ref());
    3178                insert_dep_name(&mut dep_name_set, v.dev_dependencies());
    3179                insert_dep_name(&mut dep_name_set, v.build_dependencies());
    3180            });
    3181        }
    3182        let features = manifest.features.as_mut();
    3183
    3184        let Some(features) = features else {
    3185            return;
    3186        };
    3187
    3188        features.values_mut().for_each(|feature_deps| {
    3189            feature_deps.retain(|feature_dep| {
    3190                let feature_value = FeatureValue::new(feature_dep.into());
    3191                match feature_value {
    3192                    FeatureValue::Dep { dep_name } | FeatureValue::DepFeature { dep_name, .. } => {
    3193                        let k = &manifest::PackageName::new(dep_name.to_string()).unwrap();
    3194                        dep_name_set.contains(k)
    3195                    }
    3196                    _ => true,
    3197                }
    3198            });
    3199        });
    3200    }
    3201
    3202    fn map_deps(
    3203        gctx: &GlobalContext,
    3204        deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
    3205        filter: impl Fn(&manifest::TomlDependency) -> bool,
    3206    ) -> CargoResult<Option<BTreeMap<manifest::PackageName, manifest::InheritableDependency>>> {
    3207        let Some(deps) = deps else {
    3208            return Ok(None);
    3209        };
    3210        let deps = deps
    3211            .iter()
    3212            .filter(|(_k, v)| {
    3213                if let manifest::InheritableDependency::Value(def) = v {
    3214                    filter(def)
    3215                } else {
    3216                    false
    3217                }
    3218            })
    3219            .map(|(k, v)| Ok((k.clone(), map_dependency(gctx, v)?)))
    3220            .collect::<CargoResult<BTreeMap<_, _>>>()?;
    3221        Ok(Some(deps))
    3222    }
    3223
    3224    fn map_dependency(
    3225        gctx: &GlobalContext,
    3226        dep: &manifest::InheritableDependency,
    3227    ) -> CargoResult<manifest::InheritableDependency> {
    3228        let dep = match dep {
    3229            manifest::InheritableDependency::Value(manifest::TomlDependency::Detailed(d)) => {
    3230                let mut d = d.clone();
    3231                // Path dependencies become crates.io deps.
    3232                d.path.take();
    3233                d.base.take();
    3234                // Same with git dependencies.
    3235                d.git.take();
    3236                d.branch.take();
    3237                d.tag.take();
    3238                d.rev.take();
    3239                // registry specifications are elaborated to the index URL
    3240                if let Some(registry) = d.registry.take() {
    3241                    d.registry_index = Some(gctx.get_registry_index(&registry)?.to_string());
    3242                }
    3243                Ok(d)
    3244            }
    3245            manifest::InheritableDependency::Value(manifest::TomlDependency::Simple(s)) => {
    3246                Ok(manifest::TomlDetailedDependency {
    3247                    version: Some(s.clone()),
    3248                    ..Default::default()
    3249                })
    3250            }
    3251            _ => unreachable!(),
    3252        };
    3253        dep.map(manifest::TomlDependency::Detailed)
    3254            .map(manifest::InheritableDependency::Value)
    3255    }
    3256}
    3257
    3258pub fn prepare_targets_for_publish(
    3259    targets: Option<&Vec<manifest::TomlTarget>>,
    3260    packaged_files: Option<&[PathBuf]>,
    3261    context: &str,
    3262    gctx: &GlobalContext,
    3263) -> CargoResult<Option<Vec<manifest::TomlTarget>>> {
    3264    let Some(targets) = targets else {
    3265        return Ok(None);
    3266    };
    3267
    3268    let mut prepared = Vec::with_capacity(targets.len());
    3269    for target in targets {
    3270        let Some(target) = prepare_target_for_publish(target, packaged_files, context, gctx)?
    3271        else {
    3272            continue;
    3273        };
    3274        prepared.push(target);
    3275    }
    3276
    3277    if prepared.is_empty() {
    3278        Ok(None)
    3279    } else {
    3280        Ok(Some(prepared))
    3281    }
    3282}
    3283
    3284pub fn prepare_target_for_publish(
    3285    target: &manifest::TomlTarget,
    3286    packaged_files: Option<&[PathBuf]>,
    3287    context: &str,
    3288    gctx: &GlobalContext,
    3289) -> CargoResult<Option<manifest::TomlTarget>> {
    3290    let path = target.path.as_ref().expect("previously normalized");
    3291    let path = &path.0;
    3292    if let Some(packaged_files) = packaged_files {
    3293        if !packaged_files.contains(&path) {
    3294            let name = target.name.as_ref().expect("previously normalized");
    3295            gctx.shell().warn(format!(
    3296                "ignoring {context} `{name}` as `{}` is not included in the published package",
    3297                path.display()
    3298            ))?;
    3299            return Ok(None);
    3300        }
    3301    }
    3302
    3303    let mut target = target.clone();
    3304    let path = normalize_path_sep(path.to_path_buf(), context)?;
    3305    target.path = Some(manifest::PathValue(path.into()));
    3306
    3307    Ok(Some(target))
    3308}
    3309
    3310fn normalize_path_sep(path: PathBuf, context: &str) -> CargoResult<PathBuf> {
    3311    let path = path
    3312        .into_os_string()
    3313        .into_string()
    3314        .map_err(|_err| anyhow::format_err!("non-UTF8 path for {context}"))?;
    3315    let path = normalize_path_string_sep(path);
    3316    Ok(path.into())
    3317}
    3318
    3319pub fn normalize_path_string_sep(path: String) -> String {
    3320    if std::path::MAIN_SEPARATOR != '/' {
    3321        path.replace(std::path::MAIN_SEPARATOR, "/")
    3322    } else {
    3323        path
    3324    }
    3325}