Skip to main content

cargo/util/toml/
mod.rs

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