Compare commits

...

27 Commits

Author SHA1 Message Date
Scott Schafer 8a7c93ea20
feat(cargo-lints): Add lint groups to verification 2024-04-26 20:17:30 -06:00
Scott Schafer 81ec9e629d
feat(cargo-lints): Add feature gates to lint groups 2024-04-26 20:02:06 -06:00
Scott Schafer c94e774d27
feat(cargo-lints): Add a lint verification step 2024-04-26 20:01:53 -06:00
Scott Schafer d9a7b2b260
feat(im-a-teapot): Error if specified but not enabled 2024-04-26 19:49:54 -06:00
Scott Schafer 6b5e093918
feat: Put im-a-teapot lint behind a feature 2024-04-26 19:49:50 -06:00
bors eee4ea2f5a Auto merge of #13812 - Muscraft:dont-always-inherit-workspace-lints, r=epage
fix(cargo-lints): Don't always inherit workspace lints

When working on changes for #13805, I noticed that we always passed the contents of `[workspace.lints.cargo]` into the currently implemented lints,  even if `[lints]` was not specified or did not contain `workspace = true`. This PR makes it so we only pass in the workspace cargo lints if `[lints]` contains `workspace = true`.

You can verify this change by looking at the first commit, where I added a test showing the current behavior, and looking at the second commit and seeing the test output no longer shows a warning about specifying `im-a-teapot`.
2024-04-27 00:56:12 +00:00
bors c4e19cc890 Auto merge of #13811 - ehuss:remove-sleep-test, r=weihanglo
Update SleepTraker returns_in_order unit test

This updates the `returns_in_order` SleepTracker unit test so that it is not so sensitive to how fast the system is running. Previously it assumed that the function calls would take less than a millisecond to finish, but that is not a valid assumption for a slow-running system.

I have changed it to simplify the test, with the assumption that it takes less than 30 seconds for it to run, which should have a safety margin of a few orders of magnitude.
2024-04-26 23:30:40 +00:00
Eric Huss 06fb65e753 Update SleepTraker returns_in_order unit test 2024-04-26 16:02:09 -07:00
Scott Schafer cf197fc499
fix(cargo-lints): Don't always inherit workspace lints 2024-04-26 16:37:41 -06:00
Scott Schafer c3b104e11e
test(cargo-lints): Show workspace lints always inherited 2024-04-26 16:26:36 -06:00
bors b60a155515 Auto merge of #13804 - epage:underscore, r=weihanglo
fix(toml): Remove underscore field support in 2024

### What does this PR try to resolve?

This is part of the 2024 Edition and is part of rust-lang/rust#123754 and #13629

### How should we test and review this PR?

### Additional information
2024-04-26 16:37:29 +00:00
Ed Page 8ab7683f93 refactor(fix): Be consistent in fix tracking 2024-04-26 10:57:50 -05:00
Ed Page 57b4ab90a6 refactor(toml): Consolidate field name conversion 2024-04-26 10:53:28 -05:00
bors 1dadee6d8b Auto merge of #13808 - weihanglo:buildscript-msrv, r=epage
fix: emit 1.77 syntax error only when msrv is incompatible
2024-04-26 13:49:34 +00:00
Weihang Lo ba5ec686f9
fix: emit 1.77 syntax error only when msrv is incompatible 2024-04-26 09:30:26 -04:00
Weihang Lo f70bfd3f4a
test: emit 1.77 syntax error only when msrv is incompatible 2024-04-26 09:01:56 -04:00
bors e91b58d252 Auto merge of #13794 - epage:workspace, r=weihanglo
docs(ref): Index differences between virtual / real manifests

### What does this PR try to resolve?

For a user to read the reference and to understand when each type of workspace might be right for them, they have to know to also read the section on Package Selection.

This reframes the section on needing to set `resolver = "2"` to being about differences when there isn't a root package and extends it to summarize a part of Package Selection, linking out to it. The hope is that this will make it all of the differences more discoverable without retreading too much of the same ground within Reference-style documentation.

Part of #13580

### How should we test and review this PR?

### Additional information

r? `@weihanglo`
2024-04-25 22:21:37 +00:00
Ed Page 5f5e0fc504 docs(ref): Index differences between virtual / real manifests
For a user to read the reference and to understand when each type of
workspace might be right for them, they have to know to also read the
section on Package Selection.

This reframes the section on needing to set `resolver = "2"` to being
about differences when there isn't a root package and extends it to
summarize a part of Package Selection, linking out to it.
The hope is that this will make it all of the differences more
discoverable without retreading too much of the same ground within
Reference-style documentation.

Part of #13580
2024-04-25 14:33:39 -05:00
Ed Page 50adf47c18 docs(ref): Add a caution about default-members with root package 2024-04-25 14:31:49 -05:00
Ed Page 2289026d70 docs(ref): Move default-members defalting to default-members
This also tweaks the wording to be clear that `default-members` always
apply in the workspace root, not just for virtual workspaces.
2024-04-25 14:31:27 -05:00
Ed Page 58415ff288 docs(ref): Clarify default-members and members relationship
The old statement that its a subset of `members` is incorrect because
some members can be inferred and not show up in `members`.

I tried to reword this to better convey the goal of what was being said
2024-04-25 14:27:37 -05:00
Ed Page 0b15bef075 docs(ref): Simplify default-members wording
This will make it easier to make future changes
2024-04-25 14:10:38 -05:00
Ed Page 6a213f792e docs(ref): Don't call out default-members is optional
Nothing else in the files does despite it all being optional and this
will be clearer in a follow up when I specify the defaults.
2024-04-25 14:09:26 -05:00
Ed Page a71b8fecf3 feat(fix): Migrate underscore toml fields 2024-04-25 13:22:06 -05:00
Ed Page bcf032e5be test(fix): Show underscore field migration 2024-04-25 12:44:53 -05:00
Ed Page aecb40baac fix(toml): Remove underscore field support in 2024
This is part of the 2024 Edition and is part of rust-lang/rust#123754 and #13629
2024-04-25 11:56:32 -05:00
Ed Page b81f94a8e9 refactor(toml): Move underscore validation to resolving 2024-04-24 13:32:04 -05:00
13 changed files with 1218 additions and 120 deletions

View File

@ -724,10 +724,9 @@ impl BuildOutput {
pkg_descr: &str,
msrv: &Option<RustVersion>,
) -> CargoResult<()> {
let new_syntax_added_in = &RustVersion::from_str("1.77.0")?;
if let Some(msrv) = msrv {
if msrv < new_syntax_added_in {
let new_syntax_added_in = RustVersion::from_str("1.77.0")?;
if !new_syntax_added_in.is_compatible_with(msrv.as_partial()) {
bail!(
"the `cargo::` syntax for build script output instructions was added in \
Rust 1.77.0, but the minimum supported Rust version of `{pkg_descr}` is {msrv}.\n\

View File

@ -343,7 +343,7 @@ impl FromStr for Edition {
}
}
#[derive(PartialEq)]
#[derive(Debug, PartialEq)]
enum Status {
Stable,
Unstable,
@ -387,11 +387,11 @@ macro_rules! features {
$(
$(#[$attr])*
#[doc = concat!("\n\n\nSee <https://doc.rust-lang.org/nightly/cargo/", $docs, ">.")]
pub fn $feature() -> &'static Feature {
pub const fn $feature() -> &'static Feature {
fn get(features: &Features) -> bool {
stab!($stab) == Status::Stable || features.$feature
}
static FEAT: Feature = Feature {
const FEAT: Feature = Feature {
name: stringify!($feature),
stability: stab!($stab),
version: $version,
@ -512,9 +512,10 @@ features! {
}
/// Status and metadata for a single unstable feature.
#[derive(Debug)]
pub struct Feature {
/// Feature name. This is valid Rust identifer so no dash only underscore.
name: &'static str,
pub name: &'static str,
stability: Status,
/// Version that this feature was stabilized or removed.
version: &'static str,

View File

@ -24,7 +24,9 @@ use crate::sources::{PathSource, CRATES_IO_INDEX, CRATES_IO_REGISTRY};
use crate::util::edit_distance;
use crate::util::errors::{CargoResult, ManifestError};
use crate::util::interning::InternedString;
use crate::util::lints::{check_im_a_teapot, check_implicit_features, unused_dependencies};
use crate::util::lints::{
check_im_a_teapot, check_implicit_features, unused_dependencies, verify_lints,
};
use crate::util::toml::{read_manifest, InheritableFields};
use crate::util::{
context::CargoResolverConfig, context::CargoResolverPrecedence, context::ConfigRelativePath,
@ -1147,11 +1149,26 @@ impl<'gctx> Workspace<'gctx> {
}
pub fn emit_warnings(&self) -> CargoResult<()> {
let ws_lints = self
.root_maybe()
.workspace_config()
.inheritable()
.and_then(|i| i.lints().ok())
.unwrap_or_default();
let ws_cargo_lints = ws_lints
.get("cargo")
.cloned()
.unwrap_or_default()
.into_iter()
.map(|(k, v)| (k.replace('-', "_"), v))
.collect();
for (path, maybe_pkg) in &self.packages.packages {
let path = path.join("Cargo.toml");
if let MaybePackage::Package(pkg) = maybe_pkg {
if self.gctx.cli_unstable().cargo_lints {
self.emit_lints(pkg, &path)?
self.emit_lints(pkg, &path, &ws_cargo_lints)?
}
}
let warnings = match maybe_pkg {
@ -1179,22 +1196,12 @@ impl<'gctx> Workspace<'gctx> {
Ok(())
}
pub fn emit_lints(&self, pkg: &Package, path: &Path) -> CargoResult<()> {
let ws_lints = self
.root_maybe()
.workspace_config()
.inheritable()
.and_then(|i| i.lints().ok())
.unwrap_or_default();
let ws_cargo_lints = ws_lints
.get("cargo")
.cloned()
.unwrap_or_default()
.into_iter()
.map(|(k, v)| (k.replace('-', "_"), v))
.collect();
pub fn emit_lints(
&self,
pkg: &Package,
path: &Path,
ws_cargo_lints: &manifest::TomlToolLints,
) -> CargoResult<()> {
let mut error_count = 0;
let toml_lints = pkg
.manifest()
@ -1212,11 +1219,41 @@ impl<'gctx> Workspace<'gctx> {
.map(|(name, lint)| (name.replace('-', "_"), lint))
.collect();
// We should only be using workspace lints if the `[lints]` table is
// present in the manifest, and `workspace` is set to `true`
let ws_cargo_lints = pkg
.manifest()
.resolved_toml()
.lints
.as_ref()
.is_some_and(|l| l.workspace)
.then(|| ws_cargo_lints);
let ws_contents = match self.root_maybe() {
MaybePackage::Package(pkg) => pkg.manifest().contents(),
MaybePackage::Virtual(v) => v.contents(),
};
let ws_document = match self.root_maybe() {
MaybePackage::Package(pkg) => pkg.manifest().document(),
MaybePackage::Virtual(v) => v.document(),
};
verify_lints(
pkg,
&path,
&normalized_lints,
ws_cargo_lints,
ws_contents,
ws_document,
self.root_manifest(),
self.gctx,
)?;
check_im_a_teapot(
pkg,
&path,
&normalized_lints,
&ws_cargo_lints,
ws_cargo_lints,
&mut error_count,
self.gctx,
)?;
@ -1224,7 +1261,7 @@ impl<'gctx> Workspace<'gctx> {
pkg,
&path,
&normalized_lints,
&ws_cargo_lints,
ws_cargo_lints,
&mut error_count,
self.gctx,
)?;
@ -1232,7 +1269,7 @@ impl<'gctx> Workspace<'gctx> {
pkg,
&path,
&normalized_lints,
&ws_cargo_lints,
ws_cargo_lints,
&mut error_count,
self.gctx,
)?;

View File

@ -254,9 +254,42 @@ fn migrate_manifests(ws: &Workspace<'_>, pkgs: &[&Package]) -> CargoResult<()> {
let mut fixes = 0;
let root = document.as_table_mut();
if let Some(workspace) = root
.get_mut("workspace")
.and_then(|t| t.as_table_like_mut())
{
// strictly speaking, the edition doesn't apply to this table but it should be safe
// enough
fixes += rename_dep_fields_2024(workspace, "dependencies");
}
fixes += add_feature_for_unused_deps(pkg, root);
if rename_table(root, "project", "package") {
fixes += 1;
fixes += rename_table(root, "project", "package");
if let Some(target) = root.get_mut("lib").and_then(|t| t.as_table_like_mut()) {
fixes += rename_target_fields_2024(target);
}
fixes += rename_array_of_target_fields_2024(root, "bin");
fixes += rename_array_of_target_fields_2024(root, "example");
fixes += rename_array_of_target_fields_2024(root, "test");
fixes += rename_array_of_target_fields_2024(root, "bench");
fixes += rename_dep_fields_2024(root, "dependencies");
fixes += rename_table(root, "dev_dependencies", "dev-dependencies");
fixes += rename_dep_fields_2024(root, "dev-dependencies");
fixes += rename_table(root, "build_dependencies", "build-dependencies");
fixes += rename_dep_fields_2024(root, "build-dependencies");
for target in root
.get_mut("target")
.and_then(|t| t.as_table_like_mut())
.iter_mut()
.flat_map(|t| t.iter_mut())
.filter_map(|(_k, t)| t.as_table_like_mut())
{
fixes += rename_dep_fields_2024(target, "dependencies");
fixes += rename_table(target, "dev_dependencies", "dev-dependencies");
fixes += rename_dep_fields_2024(target, "dev-dependencies");
fixes += rename_table(target, "build_dependencies", "build-dependencies");
fixes += rename_dep_fields_2024(target, "build-dependencies");
}
if 0 < fixes {
@ -274,9 +307,43 @@ fn migrate_manifests(ws: &Workspace<'_>, pkgs: &[&Package]) -> CargoResult<()> {
Ok(())
}
fn rename_table(parent: &mut dyn toml_edit::TableLike, old: &str, new: &str) -> bool {
fn rename_dep_fields_2024(parent: &mut dyn toml_edit::TableLike, dep_kind: &str) -> usize {
let mut fixes = 0;
for target in parent
.get_mut(dep_kind)
.and_then(|t| t.as_table_like_mut())
.iter_mut()
.flat_map(|t| t.iter_mut())
.filter_map(|(_k, t)| t.as_table_like_mut())
{
fixes += rename_table(target, "default_features", "default-features");
}
fixes
}
fn rename_array_of_target_fields_2024(root: &mut dyn toml_edit::TableLike, kind: &str) -> usize {
let mut fixes = 0;
for target in root
.get_mut(kind)
.and_then(|t| t.as_array_of_tables_mut())
.iter_mut()
.flat_map(|t| t.iter_mut())
{
fixes += rename_target_fields_2024(target);
}
fixes
}
fn rename_target_fields_2024(target: &mut dyn toml_edit::TableLike) -> usize {
let mut fixes = 0;
fixes += rename_table(target, "crate_type", "crate-type");
fixes += rename_table(target, "proc_macro", "proc-macro");
fixes
}
fn rename_table(parent: &mut dyn toml_edit::TableLike, old: &str, new: &str) -> usize {
let Some(old_key) = parent.key(old).cloned() else {
return false;
return 0;
};
let project = parent.remove(old).expect("returned early");
@ -286,7 +353,7 @@ fn rename_table(parent: &mut dyn toml_edit::TableLike, old: &str, new: &str) ->
*new_key.dotted_decor_mut() = old_key.dotted_decor().clone();
*new_key.leaf_decor_mut() = old_key.leaf_decor().clone();
}
true
1
}
fn add_feature_for_unused_deps(pkg: &Package, parent: &mut dyn toml_edit::TableLike) -> usize {

View File

@ -1,6 +1,6 @@
use crate::core::dependency::DepKind;
use crate::core::FeatureValue::Dep;
use crate::core::{Edition, FeatureValue, Package};
use crate::core::{Edition, Feature, FeatureValue, Manifest, Package};
use crate::util::interning::InternedString;
use crate::{CargoResult, GlobalContext};
use annotate_snippets::{Level, Renderer, Snippet};
@ -12,11 +12,202 @@ use std::ops::Range;
use std::path::Path;
use toml_edit::ImDocument;
const LINT_GROUPS: &[LintGroup] = &[TEST_DUMMY_UNSTABLE];
const LINTS: &[Lint] = &[IM_A_TEAPOT, IMPLICIT_FEATURES, UNUSED_OPTIONAL_DEPENDENCY];
pub fn verify_lints(
pkg: &Package,
path: &Path,
pkg_lints: &TomlToolLints,
ws_lints: Option<&TomlToolLints>,
ws_contents: &str,
ws_document: &ImDocument<String>,
ws_path: &Path,
gctx: &GlobalContext,
) -> CargoResult<()> {
let mut error_count = 0;
let manifest = pkg.manifest();
let manifest_path = rel_cwd_manifest_path(path, gctx);
let ws_path = rel_cwd_manifest_path(ws_path, gctx);
for lint_name in pkg_lints
.keys()
.chain(ws_lints.map(|l| l.keys()).unwrap_or_default())
{
if let Some((name, default_level, edition_lint_opts, feature_gate)) =
find_lint_or_group(lint_name)
{
let (_, reason, _) = level_priority(
name,
*default_level,
*edition_lint_opts,
pkg_lints,
ws_lints,
manifest.edition(),
);
if let Some(feature_gate) = feature_gate {
feature_gated_lint(
name,
feature_gate,
reason,
manifest,
&manifest_path,
ws_contents,
ws_document,
&ws_path,
&mut error_count,
gctx,
)?;
}
}
}
if error_count > 0 {
Err(anyhow::anyhow!(
"encountered {error_count} errors(s) while verifying lints",
))
} else {
Ok(())
}
}
fn find_lint_or_group<'a>(
name: &str,
) -> Option<(
&'static str,
&LintLevel,
&Option<(Edition, LintLevel)>,
&Option<&'static Feature>,
)> {
if let Some(lint) = LINTS.iter().find(|l| l.name == name) {
Some((
lint.name,
&lint.default_level,
&lint.edition_lint_opts,
&lint.feature_gate,
))
} else if let Some(group) = LINT_GROUPS.iter().find(|g| g.name == name) {
Some((
group.name,
&group.default_level,
&group.edition_lint_opts,
&group.feature_gate,
))
} else {
None
}
}
fn feature_gated_lint(
lint_name: &str,
feature_gate: &Feature,
reason: LintLevelReason,
manifest: &Manifest,
manifest_path: &str,
ws_contents: &str,
ws_document: &ImDocument<String>,
ws_path: &str,
error_count: &mut usize,
gctx: &GlobalContext,
) -> CargoResult<()> {
if !manifest.unstable_features().is_enabled(feature_gate) {
let dash_name = lint_name.replace("_", "-");
let dash_feature_name = feature_gate.name.replace("_", "-");
let title = format!("use of unstable lint `{}`", dash_name);
let label = format!(
"this is behind `{}`, which is not enabled",
dash_feature_name
);
let second_title = format!("`cargo::{}` was inherited", dash_name);
let help = format!(
"consider adding `cargo-features = [\"{}\"]` to the top of the manifest",
dash_feature_name
);
let message = match reason {
LintLevelReason::Package => {
let span = get_span(
manifest.document(),
&["lints", "cargo", dash_name.as_str()],
false,
)
.or(get_span(
manifest.document(),
&["lints", "cargo", lint_name],
false,
))
.unwrap();
Some(
Level::Error
.title(&title)
.snippet(
Snippet::source(manifest.contents())
.origin(&manifest_path)
.annotation(Level::Error.span(span).label(&label))
.fold(true),
)
.footer(Level::Help.title(&help)),
)
}
LintLevelReason::Workspace => {
let lint_span = get_span(
ws_document,
&["workspace", "lints", "cargo", dash_name.as_str()],
false,
)
.or(get_span(
ws_document,
&["workspace", "lints", "cargo", lint_name],
false,
))
.unwrap();
let inherit_span_key =
get_span(manifest.document(), &["lints", "workspace"], false).unwrap();
let inherit_span_value =
get_span(manifest.document(), &["lints", "workspace"], true).unwrap();
Some(
Level::Error
.title(&title)
.snippet(
Snippet::source(ws_contents)
.origin(&ws_path)
.annotation(Level::Error.span(lint_span).label(&label))
.fold(true),
)
.footer(
Level::Note.title(&second_title).snippet(
Snippet::source(manifest.contents())
.origin(&manifest_path)
.annotation(
Level::Note
.span(inherit_span_key.start..inherit_span_value.end),
)
.fold(true),
),
)
.footer(Level::Help.title(&help)),
)
}
_ => None,
};
if let Some(message) = message {
*error_count += 1;
let renderer = Renderer::styled().term_width(
gctx.shell()
.err_width()
.diagnostic_terminal_width()
.unwrap_or(annotate_snippets::renderer::DEFAULT_TERM_WIDTH),
);
writeln!(gctx.shell().err(), "{}", renderer.render(message))?;
}
}
Ok(())
}
fn get_span(document: &ImDocument<String>, path: &[&str], get_value: bool) -> Option<Range<usize>> {
let mut table = document.as_item().as_table_like().unwrap();
let mut table = document.as_item().as_table_like()?;
let mut iter = path.into_iter().peekable();
while let Some(key) = iter.next() {
let (key, item) = table.get_key_value(key).unwrap();
let (key, item) = table.get_key_value(key)?;
if iter.peek().is_none() {
return if get_value {
item.span()
@ -66,6 +257,7 @@ pub struct LintGroup {
pub default_level: LintLevel,
pub desc: &'static str,
pub edition_lint_opts: Option<(Edition, LintLevel)>,
pub feature_gate: Option<&'static Feature>,
}
const TEST_DUMMY_UNSTABLE: LintGroup = LintGroup {
@ -73,6 +265,7 @@ const TEST_DUMMY_UNSTABLE: LintGroup = LintGroup {
desc: "test_dummy_unstable is meant to only be used in tests",
default_level: LintLevel::Allow,
edition_lint_opts: None,
feature_gate: Some(Feature::test_dummy_unstable()),
};
#[derive(Copy, Clone, Debug)]
@ -82,13 +275,14 @@ pub struct Lint {
pub groups: &'static [LintGroup],
pub default_level: LintLevel,
pub edition_lint_opts: Option<(Edition, LintLevel)>,
pub feature_gate: Option<&'static Feature>,
}
impl Lint {
pub fn level(
&self,
pkg_lints: &TomlToolLints,
ws_lints: &TomlToolLints,
ws_lints: Option<&TomlToolLints>,
edition: Edition,
) -> (LintLevel, LintLevelReason) {
self.groups
@ -164,7 +358,7 @@ impl From<TomlLintLevel> for LintLevel {
}
}
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LintLevelReason {
Default,
Edition(Edition),
@ -188,7 +382,7 @@ fn level_priority(
default_level: LintLevel,
edition_lint_opts: Option<(Edition, LintLevel)>,
pkg_lints: &TomlToolLints,
ws_lints: &TomlToolLints,
ws_lints: Option<&TomlToolLints>,
edition: Edition,
) -> (LintLevel, LintLevelReason, i8) {
let (unspecified_level, reason) = if let Some(level) = edition_lint_opts
@ -211,7 +405,7 @@ fn level_priority(
LintLevelReason::Package,
defined_level.priority(),
)
} else if let Some(defined_level) = ws_lints.get(name) {
} else if let Some(defined_level) = ws_lints.and_then(|l| l.get(name)) {
(
defined_level.level().into(),
LintLevelReason::Workspace,
@ -228,18 +422,21 @@ const IM_A_TEAPOT: Lint = Lint {
groups: &[TEST_DUMMY_UNSTABLE],
default_level: LintLevel::Allow,
edition_lint_opts: None,
feature_gate: Some(Feature::test_dummy_unstable()),
};
pub fn check_im_a_teapot(
pkg: &Package,
path: &Path,
pkg_lints: &TomlToolLints,
ws_lints: &TomlToolLints,
ws_lints: Option<&TomlToolLints>,
error_count: &mut usize,
gctx: &GlobalContext,
) -> CargoResult<()> {
let manifest = pkg.manifest();
let manifest_path = rel_cwd_manifest_path(path, gctx);
let (lint_level, reason) = IM_A_TEAPOT.level(pkg_lints, ws_lints, manifest.edition());
if lint_level == LintLevel::Allow {
return Ok(());
}
@ -253,7 +450,6 @@ pub fn check_im_a_teapot(
*error_count += 1;
}
let level = lint_level.to_diagnostic_level();
let manifest_path = rel_cwd_manifest_path(path, gctx);
let emitted_reason = format!(
"`cargo::{}` is set to `{lint_level}` {reason}",
IM_A_TEAPOT.name
@ -300,13 +496,14 @@ const IMPLICIT_FEATURES: Lint = Lint {
groups: &[],
default_level: LintLevel::Allow,
edition_lint_opts: None,
feature_gate: None,
};
pub fn check_implicit_features(
pkg: &Package,
path: &Path,
pkg_lints: &TomlToolLints,
ws_lints: &TomlToolLints,
ws_lints: Option<&TomlToolLints>,
error_count: &mut usize,
gctx: &GlobalContext,
) -> CargoResult<()> {
@ -384,13 +581,14 @@ const UNUSED_OPTIONAL_DEPENDENCY: Lint = Lint {
groups: &[],
default_level: LintLevel::Warn,
edition_lint_opts: None,
feature_gate: None,
};
pub fn unused_dependencies(
pkg: &Package,
path: &Path,
pkg_lints: &TomlToolLints,
ws_lints: &TomlToolLints,
ws_lints: Option<&TomlToolLints>,
error_count: &mut usize,
gctx: &GlobalContext,
) -> CargoResult<()> {

View File

@ -90,13 +90,15 @@ impl<T> SleepTracker<T> {
#[test]
fn returns_in_order() {
let mut s = SleepTracker::new();
s.push(3, 3);
s.push(30_000, 30_000);
s.push(1, 1);
s.push(6, 6);
s.push(5, 5);
s.push(2, 2);
s.push(10000, 10000);
assert_eq!(s.len(), 6);
std::thread::sleep(Duration::from_millis(100));
assert_eq!(s.to_retry(), &[1, 2, 3, 5, 6]);
assert_eq!(s.len(), 2);
std::thread::sleep(Duration::from_millis(2));
assert_eq!(s.to_retry(), &[1]);
assert!(s.to_retry().is_empty());
let next = s.time_to_next().expect("should be next");
assert!(
next < Duration::from_millis(30_000),
"{next:?} should be less than 30s"
);
}

View File

@ -306,6 +306,8 @@ fn resolve_toml(
};
if let Some(original_package) = original_toml.package() {
let package_name = &original_package.name;
let resolved_package =
resolve_package_toml(original_package, features, package_root, &inherit)?;
let edition = resolved_package
@ -341,6 +343,15 @@ fn resolve_toml(
package_root,
warnings,
)?;
deprecated_underscore(
&original_toml.dev_dependencies2,
&original_toml.dev_dependencies,
"dev-dependencies",
package_name,
"package",
edition,
warnings,
)?;
resolved_toml.dev_dependencies = resolve_dependencies(
gctx,
edition,
@ -352,6 +363,15 @@ fn resolve_toml(
package_root,
warnings,
)?;
deprecated_underscore(
&original_toml.build_dependencies2,
&original_toml.build_dependencies,
"build-dependencies",
package_name,
"package",
edition,
warnings,
)?;
resolved_toml.build_dependencies = resolve_dependencies(
gctx,
edition,
@ -376,6 +396,15 @@ fn resolve_toml(
package_root,
warnings,
)?;
deprecated_underscore(
&platform.dev_dependencies2,
&platform.dev_dependencies,
"dev-dependencies",
name,
"platform target",
edition,
warnings,
)?;
let resolved_dev_dependencies = resolve_dependencies(
gctx,
edition,
@ -387,6 +416,15 @@ fn resolve_toml(
package_root,
warnings,
)?;
deprecated_underscore(
&platform.build_dependencies2,
&platform.build_dependencies,
"build-dependencies",
name,
"platform target",
edition,
warnings,
)?;
let resolved_build_dependencies = resolve_dependencies(
gctx,
edition,
@ -617,6 +655,15 @@ fn resolve_dependencies<'a>(
let mut resolved =
dependency_inherit_with(v.clone(), name_in_toml, inherit, package_root, warnings)?;
if let manifest::TomlDependency::Detailed(ref mut d) = resolved {
deprecated_underscore(
&d.default_features2,
&d.default_features,
"default-features",
name_in_toml,
"dependency",
edition,
warnings,
)?;
if d.public.is_some() {
let public_feature = features.require(Feature::public_dependency());
let with_public_feature = public_feature.is_ok();
@ -1146,28 +1193,12 @@ fn to_real_manifest(
}
validate_dependencies(original_toml.dependencies.as_ref(), None, None, warnings)?;
deprecated_underscore(
&original_toml.dev_dependencies2,
&original_toml.dev_dependencies,
"dev-dependencies",
package_name,
"package",
warnings,
);
validate_dependencies(
original_toml.dev_dependencies(),
None,
Some(DepKind::Development),
warnings,
)?;
deprecated_underscore(
&original_toml.build_dependencies2,
&original_toml.build_dependencies,
"build-dependencies",
package_name,
"package",
warnings,
);
validate_dependencies(
original_toml.build_dependencies(),
None,
@ -1184,28 +1215,12 @@ fn to_real_manifest(
None,
warnings,
)?;
deprecated_underscore(
&platform.build_dependencies2,
&platform.build_dependencies,
"build-dependencies",
name,
"platform target",
warnings,
);
validate_dependencies(
platform.build_dependencies(),
platform_kind.as_ref(),
Some(DepKind::Build),
warnings,
)?;
deprecated_underscore(
&platform.dev_dependencies2,
&platform.dev_dependencies,
"dev-dependencies",
name,
"platform target",
warnings,
);
validate_dependencies(
platform.dev_dependencies(),
platform_kind.as_ref(),
@ -1811,14 +1826,6 @@ fn detailed_dep_to_dependency<P: ResolveToPath + Clone>(
let version = orig.version.as_deref();
let mut dep = Dependency::parse(pkg_name, version, new_source_id)?;
deprecated_underscore(
&orig.default_features2,
&orig.default_features,
"default-features",
name_in_toml,
"dependency",
manifest_ctx.warnings,
);
dep.set_features(orig.features.iter().flatten())
.set_default_features(orig.default_features().unwrap_or(true))
.set_optional(orig.optional.unwrap_or(false))
@ -2321,19 +2328,22 @@ fn deprecated_underscore<T>(
new_path: &str,
name: &str,
kind: &str,
edition: Edition,
warnings: &mut Vec<String>,
) {
if old.is_some() && new.is_some() {
let old_path = new_path.replace("-", "_");
) -> CargoResult<()> {
let old_path = new_path.replace("-", "_");
if old.is_some() && Edition::Edition2024 <= edition {
anyhow::bail!("`{old_path}` is unsupported as of the 2024 edition; instead use `{new_path}`\n(in the `{name}` {kind})");
} else if old.is_some() && new.is_some() {
warnings.push(format!(
"`{old_path}` is redundant with `{new_path}`, preferring `{new_path}` in the `{name}` {kind}"
))
} else if old.is_some() {
let old_path = new_path.replace("-", "_");
warnings.push(format!(
"`{old_path}` is deprecated in favor of `{new_path}` and will not work in the 2024 edition\n(in the `{name}` {kind})"
))
}
Ok(())
}
fn warn_on_unused(unused: &BTreeSet<String>, warnings: &mut Vec<String>) {

View File

@ -97,12 +97,7 @@ pub(super) fn to_targets(
warnings,
errors,
)?;
targets.extend(to_example_targets(
&toml_examples,
package_root,
edition,
warnings,
)?);
targets.extend(to_example_targets(&toml_examples, package_root, edition)?);
let toml_tests = resolve_tests(
resolved_toml.test.as_ref(),
@ -183,6 +178,10 @@ fn resolve_lib(
// Check early to improve error messages
validate_lib_name(&lib, warnings)?;
// Checking the original lib
validate_proc_macro(&lib, "library", edition, warnings)?;
validate_crate_types(&lib, "library", edition, warnings)?;
if lib.path.is_none() {
if let Some(inferred) = inferred {
lib.path = Some(PathValue(inferred));
@ -218,8 +217,6 @@ fn to_lib_target(
let Some(lib) = resolved_lib else {
return Ok(None);
};
validate_proc_macro(lib, "library", warnings);
validate_crate_types(lib, "library", warnings);
let path = lib.path.as_ref().expect("previously resolved");
let path = package_root.join(&path.0);
@ -442,14 +439,12 @@ fn to_example_targets(
targets: &[TomlExampleTarget],
package_root: &Path,
edition: Edition,
warnings: &mut Vec<String>,
) -> CargoResult<Vec<Target>> {
validate_unique_names(&targets, "example")?;
let mut result = Vec::new();
for toml in targets {
let path = package_root.join(&toml.path.as_ref().expect("previously resolved").0);
validate_crate_types(&toml, "example", warnings);
let crate_types = match toml.crate_types() {
Some(kinds) => kinds.iter().map(|s| s.into()).collect(),
None => Vec::new(),
@ -637,6 +632,8 @@ fn resolve_targets_with_legacy_path(
for target in &toml_targets {
validate_target_name(target, target_kind_human, target_kind, warnings)?;
validate_proc_macro(target, target_kind_human, edition, warnings)?;
validate_crate_types(target, target_kind_human, edition, warnings)?;
}
let mut result = Vec::new();
@ -1101,24 +1098,36 @@ fn name_or_panic(target: &TomlTarget) -> &str {
.unwrap_or_else(|| panic!("target name is required"))
}
fn validate_proc_macro(target: &TomlTarget, kind: &str, warnings: &mut Vec<String>) {
fn validate_proc_macro(
target: &TomlTarget,
kind: &str,
edition: Edition,
warnings: &mut Vec<String>,
) -> CargoResult<()> {
deprecated_underscore(
&target.proc_macro2,
&target.proc_macro,
"proc-macro",
name_or_panic(target),
format!("{kind} target").as_str(),
edition,
warnings,
);
)
}
fn validate_crate_types(target: &TomlTarget, kind: &str, warnings: &mut Vec<String>) {
fn validate_crate_types(
target: &TomlTarget,
kind: &str,
edition: Edition,
warnings: &mut Vec<String>,
) -> CargoResult<()> {
deprecated_underscore(
&target.crate_type2,
&target.crate_type,
"crate-type",
name_or_panic(target),
format!("{kind} target").as_str(),
edition,
warnings,
);
)
}

View File

@ -80,10 +80,14 @@ edition = "2021" # the edition, will have no effect on a resolver used in th
authors = ["Alice <a@example.com>", "Bob <b@example.com>"]
```
Note that in a virtual manifest the [`resolver = "2"`](resolver.md#resolver-versions)
should be specified manually. It is usually deduced from the [`package.edition`][package-edition]
field which is absent in virtual manifests and the edition field of a member
won't affect the resolver used by the workspace.
By having a workspace without a root package,
- [`resolver`](resolver.md#resolver-versions) must be
set explicitly in virtual workspaces as they have no
[`package.edition`][package-edition] to infer it from
[resolver version](resolver.md#resolver-versions).
- Commands run in the workspace root will run against all workspace
members by default, see [`default-members`](#the-default-members-field).
## The `members` and `exclude` fields
@ -120,14 +124,12 @@ is not inside a subdirectory of the workspace root.
In a workspace, package-related Cargo commands like [`cargo build`] can use
the `-p` / `--package` or `--workspace` command-line flags to determine which
packages to operate on. If neither of those flags are specified, Cargo will
use the package in the current working directory. If the current directory is
a [virtual workspace](#virtual-workspace), it will apply to all members (as if
`--workspace` were specified on the command-line). See also
[`default-members`](#the-default-members-field).
use the package in the current working directory. However, if the current directory is
a workspace root, the [`default-members`](#the-default-members-field) will be used.
## The `default-members` field
The optional `default-members` key can be specified to set the members to
The `default-members` field specifies paths of [members](#the-members-and-exclude-fields) to
operate on when in the workspace root and the package selection flags are not
used:
@ -137,7 +139,12 @@ members = ["path/to/member1", "path/to/member2", "path/to/member3/*"]
default-members = ["path/to/member2", "path/to/member3/foo"]
```
When specified, `default-members` must expand to a subset of `members`.
> Note: when a [root package](#root-package) is present,
> you can only operate on it using `--package` and `--workspace` flags.
When unspecified, the [root package](#root-package) will be used.
In the case of a [virtual workspace](#virtual-workspace), all members will be used
(as if `--workspace` were specified on the command-line).
## The `package` table

View File

@ -841,6 +841,50 @@ fn dev_dependencies2() {
.run();
}
#[cargo_test(nightly, reason = "edition2024 is not stable")]
fn dev_dependencies2_2024() {
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["edition2024"]
[package]
name = "foo"
version = "0.1.0"
edition = "2024"
[dev_dependencies]
a = {path = "a"}
"#,
)
.file("src/lib.rs", "")
.file(
"a/Cargo.toml",
r#"
[package]
name = "a"
version = "0.0.1"
edition = "2015"
"#,
)
.file("a/src/lib.rs", "")
.build();
p.cargo("check")
.masquerade_as_nightly_cargo(&["edition2024"])
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
Caused by:
`dev_dependencies` is unsupported as of the 2024 edition; instead use `dev-dependencies`
(in the `foo` package)
",
)
.run();
}
#[cargo_test]
fn dev_dependencies2_conflict() {
let p = project()
@ -916,6 +960,50 @@ fn build_dependencies2() {
.run();
}
#[cargo_test(nightly, reason = "edition2024 is not stable")]
fn build_dependencies2_2024() {
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["edition2024"]
[package]
name = "foo"
version = "0.1.0"
edition = "2024"
[build_dependencies]
a = {path = "a"}
"#,
)
.file("src/lib.rs", "")
.file(
"a/Cargo.toml",
r#"
[package]
name = "a"
version = "0.0.1"
edition = "2015"
"#,
)
.file("a/src/lib.rs", "")
.build();
p.cargo("check")
.masquerade_as_nightly_cargo(&["edition2024"])
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
Caused by:
`build_dependencies` is unsupported as of the 2024 edition; instead use `build-dependencies`
(in the `foo` package)
",
)
.run();
}
#[cargo_test]
fn build_dependencies2_conflict() {
let p = project()
@ -983,6 +1071,42 @@ fn lib_crate_type2() {
.run();
}
#[cargo_test(nightly, reason = "edition2024 is not stable")]
fn lib_crate_type2_2024() {
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["edition2024"]
[package]
name = "foo"
version = "0.5.0"
edition = "2024"
authors = ["wycats@example.com"]
[lib]
name = "foo"
crate_type = ["staticlib", "dylib"]
"#,
)
.file("src/lib.rs", "pub fn foo() {}")
.build();
p.cargo("check")
.masquerade_as_nightly_cargo(&["edition2024"])
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
Caused by:
`crate_type` is unsupported as of the 2024 edition; instead use `crate-type`
(in the `foo` library target)
",
)
.run();
}
#[cargo_test]
fn lib_crate_type2_conflict() {
let p = project()
@ -1060,6 +1184,59 @@ fn examples_crate_type2() {
.run();
}
#[cargo_test(nightly, reason = "edition2024 is not stable")]
fn examples_crate_type2_2024() {
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["edition2024"]
[package]
name = "foo"
version = "0.5.0"
edition = "2024"
authors = ["wycats@example.com"]
[[example]]
name = "ex"
path = "examples/ex.rs"
crate_type = ["proc_macro"]
[[example]]
name = "goodbye"
path = "examples/ex-goodbye.rs"
crate_type = ["rlib", "staticlib"]
"#,
)
.file("src/lib.rs", "")
.file(
"examples/ex.rs",
r#"
fn main() { println!("ex"); }
"#,
)
.file(
"examples/ex-goodbye.rs",
r#"
fn main() { println!("goodbye"); }
"#,
)
.build();
p.cargo("check")
.masquerade_as_nightly_cargo(&["edition2024"])
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
Caused by:
`crate_type` is unsupported as of the 2024 edition; instead use `crate-type`
(in the `ex` example target)
",
)
.run();
}
#[cargo_test]
fn examples_crate_type2_conflict() {
let p = project()
@ -1148,6 +1325,53 @@ fn cargo_platform_build_dependencies2() {
.run();
}
#[cargo_test(nightly, reason = "edition2024 is not stable")]
fn cargo_platform_build_dependencies2_2024() {
let host = rustc_host();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
cargo-features = ["edition2024"]
[package]
name = "foo"
version = "0.5.0"
edition = "2024"
authors = ["wycats@example.com"]
build = "build.rs"
[target.{host}.build_dependencies]
build = {{ path = "build" }}
"#,
host = host
),
)
.file("src/main.rs", "fn main() { }")
.file(
"build.rs",
"extern crate build; fn main() { build::build(); }",
)
.file("build/Cargo.toml", &basic_manifest("build", "0.5.0"))
.file("build/src/lib.rs", "pub fn build() {}")
.build();
p.cargo("check")
.masquerade_as_nightly_cargo(&["edition2024"])
.with_status(101)
.with_stderr(format!(
"\
[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
Caused by:
`build_dependencies` is unsupported as of the 2024 edition; instead use `build-dependencies`
(in the `{host}` platform target)
"
))
.run();
}
#[cargo_test]
fn cargo_platform_build_dependencies2_conflict() {
let host = rustc_host();
@ -1228,6 +1452,52 @@ fn cargo_platform_dev_dependencies2() {
.run();
}
#[cargo_test(nightly, reason = "edition2024 is not stable")]
fn cargo_platform_dev_dependencies2_2024() {
let host = rustc_host();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
cargo-features = ["edition2024"]
[package]
name = "foo"
version = "0.5.0"
edition = "2024"
authors = ["wycats@example.com"]
[target.{host}.dev_dependencies]
dev = {{ path = "dev" }}
"#,
host = host
),
)
.file("src/main.rs", "fn main() { }")
.file(
"tests/foo.rs",
"extern crate dev; #[test] fn foo() { dev::dev() }",
)
.file("dev/Cargo.toml", &basic_manifest("dev", "0.5.0"))
.file("dev/src/lib.rs", "pub fn dev() {}")
.build();
p.cargo("check")
.masquerade_as_nightly_cargo(&["edition2024"])
.with_status(101)
.with_stderr(format!(
"\
[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
Caused by:
`dev_dependencies` is unsupported as of the 2024 edition; instead use `dev-dependencies`
(in the `{host}` platform target)
"
))
.run();
}
#[cargo_test]
fn cargo_platform_dev_dependencies2_conflict() {
let host = rustc_host();
@ -1312,6 +1582,57 @@ fn default_features2() {
.run();
}
#[cargo_test(nightly, reason = "edition2024 is not stable")]
fn default_features2_2024() {
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["edition2024"]
[package]
name = "foo"
version = "0.1.0"
edition = "2024"
authors = []
[dependencies]
a = { path = "a", features = ["f1"], default_features = false }
"#,
)
.file("src/lib.rs", "")
.file(
"a/Cargo.toml",
r#"
[package]
name = "a"
version = "0.1.0"
edition = "2015"
authors = []
[features]
default = ["f1"]
f1 = []
"#,
)
.file("a/src/lib.rs", "")
.build();
p.cargo("check")
.masquerade_as_nightly_cargo(&["edition2024"])
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
Caused by:
`default_features` is unsupported as of the 2024 edition; instead use `default-features`
(in the `a` dependency)
",
)
.run();
}
#[cargo_test]
fn default_features2_conflict() {
let p = project()
@ -1437,6 +1758,99 @@ warning: [CWD]/workspace_only/Cargo.toml: `default_features` is deprecated in fa
.run();
}
#[cargo_test(nightly, reason = "edition2024 is not stable")]
fn workspace_default_features2_2024() {
let p = project()
.file(
"Cargo.toml",
r#"
[workspace]
members = ["workspace_only", "dep_workspace_only", "package_only", "dep_package_only"]
[workspace.dependencies]
dep_workspace_only = { path = "dep_workspace_only", default_features = true }
dep_package_only = { path = "dep_package_only" }
"#,
)
.file(
"workspace_only/Cargo.toml",
r#"
cargo-features = ["edition2024"]
[package]
name = "workspace_only"
version = "0.1.0"
edition = "2024"
authors = []
[dependencies]
dep_workspace_only.workspace = true
"#,
)
.file("workspace_only/src/lib.rs", "")
.file(
"dep_workspace_only/Cargo.toml",
r#"
cargo-features = ["edition2024"]
[package]
name = "dep_workspace_only"
version = "0.1.0"
edition = "2024"
authors = []
"#,
)
.file("dep_workspace_only/src/lib.rs", "")
.file(
"package_only/Cargo.toml",
r#"
cargo-features = ["edition2024"]
[package]
name = "package_only"
version = "0.1.0"
edition = "2024"
authors = []
[dependencies]
dep_package_only = { workspace = true, default_features = true }
"#,
)
.file("package_only/src/lib.rs", "")
.file(
"dep_package_only/Cargo.toml",
r#"
cargo-features = ["edition2024"]
[package]
name = "dep_package_only"
version = "0.1.0"
edition = "2024"
authors = []
"#,
)
.file("dep_package_only/src/lib.rs", "")
.build();
p.cargo("check")
.masquerade_as_nightly_cargo(&["edition2024"])
.with_status(101)
.with_stderr(
"\
[ERROR] failed to load manifest for workspace member `[CWD]/workspace_only`
referenced by workspace at `[CWD]/Cargo.toml`
Caused by:
failed to parse manifest at `[CWD]/workspace_only/Cargo.toml`
Caused by:
`default_features` is unsupported as of the 2024 edition; instead use `default-features`
(in the `dep_workspace_only` dependency)
",
)
.run();
}
#[cargo_test]
fn proc_macro2() {
let foo = project()
@ -1464,6 +1878,40 @@ fn proc_macro2() {
.run();
}
#[cargo_test(nightly, reason = "edition2024 is not stable")]
fn proc_macro2_2024() {
let foo = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["edition2024"]
[package]
name = "foo"
version = "0.1.0"
edition = "2024"
[lib]
proc_macro = true
"#,
)
.file("src/lib.rs", "")
.build();
foo.cargo("check")
.masquerade_as_nightly_cargo(&["edition2024"])
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
Caused by:
`proc_macro` is unsupported as of the 2024 edition; instead use `proc-macro`
(in the `foo` library target)
",
)
.run();
}
#[cargo_test]
fn proc_macro2_conflict() {
let foo = project()

View File

@ -5502,6 +5502,39 @@ for more information about build script outputs.
.run();
}
#[cargo_test]
fn test_new_syntax_with_compatible_partial_msrv() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
edition = "2015"
build = "build.rs"
rust-version = "1.77"
"#,
)
.file("src/lib.rs", "")
.file(
"build.rs",
r#"
fn main() {
println!("cargo::metadata=foo=bar");
}
"#,
)
.build();
p.cargo("check")
.with_stderr_contains(
"\
[COMPILING] foo [..]
",
)
.run();
}
#[cargo_test]
fn test_old_syntax_with_old_msrv() {
let p = project()

View File

@ -2050,6 +2050,149 @@ edition = "2021"
);
}
#[cargo_test]
fn migrate_rename_underscore_fields() {
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["edition2024"]
[workspace.dependencies]
# Before default_features
a = {path = "a", default_features = false} # After default_features value
# After default_features line
[package]
name = "foo"
edition = "2021"
[lib]
name = "foo"
# Before crate_type
crate_type = ["staticlib", "dylib"] # After crate_type value
# After crate_type line
[[example]]
name = "ex"
path = "examples/ex.rs"
# Before crate_type
crate_type = ["proc-macro"] # After crate_type value
# After crate_type line
# Before dev_dependencies
[ dev_dependencies ] # After dev_dependencies header
# After dev_dependencies line
a = {path = "a", default_features = false}
# After dev_dependencies table
# Before build_dependencies
[ build_dependencies ] # After build_dependencies header
# After build_dependencies line
a = {path = "a", default_features = false}
# After build_dependencies table
# Before dev_dependencies
[ target.'cfg(any())'.dev_dependencies ] # After dev_dependencies header
# After dev_dependencies line
a = {path = "a", default_features = false}
# After dev_dependencies table
# Before build_dependencies
[ target.'cfg(any())'.build_dependencies ] # After build_dependencies header
# After build_dependencies line
a = {path = "a", default_features = false}
# After build_dependencies table
"#,
)
.file("src/lib.rs", "")
.file(
"examples/ex.rs",
r#"
fn main() { println!("ex"); }
"#,
)
.file(
"a/Cargo.toml",
r#"
[package]
name = "a"
version = "0.0.1"
edition = "2015"
"#,
)
.file("a/src/lib.rs", "")
.build();
p.cargo("fix --edition --allow-no-vcs")
.masquerade_as_nightly_cargo(&["edition2024"])
.with_stderr(
"\
[MIGRATING] Cargo.toml from 2021 edition to 2024
[FIXED] Cargo.toml (11 fixes)
Locking 2 packages to latest compatible versions
Checking a v0.0.1 ([CWD]/a)
[CHECKING] foo v0.0.0 ([CWD])
[MIGRATING] src/lib.rs from 2021 edition to 2024
[MIGRATING] examples/ex.rs from 2021 edition to 2024
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [..]s
",
)
.run();
assert_eq!(
p.read_file("Cargo.toml"),
r#"
cargo-features = ["edition2024"]
[workspace.dependencies]
# Before default_features
a = {path = "a", default-features = false} # After default_features value
# After default_features line
[package]
name = "foo"
edition = "2021"
[lib]
name = "foo"
# Before crate_type
crate-type = ["staticlib", "dylib"] # After crate_type value
# After crate_type line
[[example]]
name = "ex"
path = "examples/ex.rs"
# Before crate_type
crate-type = ["proc-macro"] # After crate_type value
# After crate_type line
# Before dev_dependencies
[ dev-dependencies ] # After dev_dependencies header
# After dev_dependencies line
a = {path = "a", default-features = false}
# After dev_dependencies table
# Before build_dependencies
[ build-dependencies ] # After build_dependencies header
# After build_dependencies line
a = {path = "a", default-features = false}
# After build_dependencies table
# Before dev_dependencies
[ target.'cfg(any())'.dev-dependencies ] # After dev_dependencies header
# After dev_dependencies line
a = {path = "a", default-features = false}
# After dev_dependencies table
# Before build_dependencies
[ target.'cfg(any())'.build-dependencies ] # After build_dependencies header
# After build_dependencies line
a = {path = "a", default-features = false}
# After build_dependencies table
"#,
);
}
#[cargo_test]
fn add_feature_for_unused_dep() {
Package::new("bar", "0.1.0").publish();

View File

@ -982,3 +982,147 @@ error: `im_a_teapot` is specified
)
.run();
}
#[cargo_test]
fn dont_always_inherit_workspace_lints() {
let p = project()
.file(
"Cargo.toml",
r#"
[workspace]
members = ["foo"]
[workspace.lints.cargo]
im-a-teapot = "warn"
"#,
)
.file(
"foo/Cargo.toml",
r#"
cargo-features = ["test-dummy-unstable"]
[package]
name = "foo"
version = "0.0.1"
edition = "2015"
authors = []
im-a-teapot = true
"#,
)
.file("foo/src/lib.rs", "")
.build();
p.cargo("check -Zcargo-lints")
.masquerade_as_nightly_cargo(&["cargo-lints"])
.with_stderr(
"\
[CHECKING] foo v0.0.1 ([CWD]/foo)
[FINISHED] [..]
",
)
.run();
}
#[cargo_test]
fn check_feature_gated() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
edition = "2015"
authors = []
[lints.cargo]
im-a-teapot = "warn"
"#,
)
.file("src/lib.rs", "")
.build();
p.cargo("check -Zcargo-lints")
.masquerade_as_nightly_cargo(&["cargo-lints"])
.with_status(101)
.with_stderr(
"\
error: use of unstable lint `im-a-teapot`
--> Cargo.toml:9:1
|
9 | im-a-teapot = \"warn\"
| ^^^^^^^^^^^ this is behind `test-dummy-unstable`, which is not enabled
|
= help: consider adding `cargo-features = [\"test-dummy-unstable\"]` to the top of the manifest
error: encountered 1 errors(s) while verifying lints
",
)
.run();
}
#[cargo_test]
fn check_feature_gated_workspace() {
let p = project()
.file(
"Cargo.toml",
r#"
[workspace]
members = ["foo"]
[workspace.lints.cargo]
im-a-teapot = { level = "warn", priority = 10 }
test-dummy-unstable = { level = "forbid", priority = -1 }
"#,
)
.file(
"foo/Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
edition = "2015"
authors = []
[lints]
workspace = true
"#,
)
.file("foo/src/lib.rs", "")
.build();
p.cargo("check -Zcargo-lints")
.masquerade_as_nightly_cargo(&["cargo-lints"])
.with_status(101)
.with_stderr(
"\
error: use of unstable lint `im-a-teapot`
--> Cargo.toml:6:1
|
6 | im-a-teapot = { level = \"warn\", priority = 10 }
| ^^^^^^^^^^^ this is behind `test-dummy-unstable`, which is not enabled
|
note: `cargo::im-a-teapot` was inherited
--> foo/Cargo.toml:9:1
|
9 | workspace = true
| ----------------
|
= help: consider adding `cargo-features = [\"test-dummy-unstable\"]` to the top of the manifest
error: use of unstable lint `test-dummy-unstable`
--> Cargo.toml:7:1
|
7 | test-dummy-unstable = { level = \"forbid\", priority = -1 }
| ^^^^^^^^^^^^^^^^^^^ this is behind `test-dummy-unstable`, which is not enabled
|
note: `cargo::test-dummy-unstable` was inherited
--> foo/Cargo.toml:9:1
|
9 | workspace = true
| ----------------
|
= help: consider adding `cargo-features = [\"test-dummy-unstable\"]` to the top of the manifest
error: encountered 2 errors(s) while verifying lints
",
)
.run();
}