#
cargo/util/toml/
mod.rs

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