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