Skip to main content

cargo/workspace/parser/
mod.rs

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