#
cargo/util/toml/
mod.rs

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