cargo/util/toml/
mod.rs

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