#
cargo/util/toml/
mod.rs

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