#
cargo/util/toml/
mod.rs

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