Skip to main content

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