cargo/util/toml/
mod.rs

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