Skip to main content

cargo/util/toml/
mod.rs

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