Skip to main content

cargo/util/toml/
mod.rs

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