#
cargo/util/toml/
mod.rs

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