Compare commits

...

8 Commits

Author SHA1 Message Date
Scott Schafer caf4400467
Merge 0b21cd2145 into c4e19cc890 2024-04-27 02:04:21 +02: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 0b21cd2145
feat(cargo-lints): Add lint groups to verification 2024-04-26 15:49:17 -06:00
Scott Schafer 889f5ab15b
feat(cargo-lints): Add feature gates to lint groups 2024-04-26 15:47:08 -06:00
Scott Schafer 9c797763e7
feat(cargo-lints): Add a lint verification step 2024-04-26 15:47:07 -06:00
Scott Schafer ab8855487d
feat(im-a-teapot): Error if specified but not enabled 2024-04-26 15:47:06 -06:00
Scott Schafer dc4cd7fe4d
feat: Put im-a-teapot lint behind a feature 2024-04-26 15:47:05 -06:00
5 changed files with 303 additions and 17 deletions

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,6 +512,7 @@ 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,

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,
@ -1180,6 +1182,16 @@ impl<'gctx> Workspace<'gctx> {
}
pub fn emit_lints(&self, pkg: &Package, path: &Path) -> CargoResult<()> {
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(),
};
let ws_lints = self
.root_maybe()
.workspace_config()
@ -1212,6 +1224,16 @@ impl<'gctx> Workspace<'gctx> {
.map(|(name, lint)| (name.replace('-', "_"), lint))
.collect();
verify_lints(
pkg,
&path,
&normalized_lints,
&ws_cargo_lints,
ws_contents,
ws_document,
self.root_manifest(),
self.gctx,
)?;
check_im_a_teapot(
pkg,
&path,

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,164 @@ 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: &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);
let iter = LINTS
.iter()
.map(|l| (l.name, l.default_level, l.edition_lint_opts, l.feature_gate))
.chain(
LINT_GROUPS
.iter()
.map(|g| (g.name, g.default_level, g.edition_lint_opts, g.feature_gate)),
)
.collect::<Vec<_>>();
for lint_name in pkg_lints.keys().chain(ws_lints.keys()) {
if let Some((name, default_level, edition_lint_opts, feature_gate)) =
iter.iter().find(|(n, _, _, _)| n == &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 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 title = "verification of lint failed";
let label = "use of unstable lint that has not been enabled";
let second_title = format!("`cargo::{}` was inherited", lint_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),
),
)
}
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 =
get_span(manifest.document(), &["lints", "workspace"], false).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))
.fold(true),
),
),
)
}
_ => 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 +219,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 +227,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,6 +237,7 @@ 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 {
@ -164,7 +320,7 @@ impl From<TomlLintLevel> for LintLevel {
}
}
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LintLevelReason {
Default,
Edition(Edition),
@ -228,6 +384,7 @@ 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(
@ -239,7 +396,9 @@ pub fn check_im_a_teapot(
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 +412,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,6 +458,7 @@ const IMPLICIT_FEATURES: Lint = Lint {
groups: &[],
default_level: LintLevel::Allow,
edition_lint_opts: None,
feature_gate: None,
};
pub fn check_implicit_features(
@ -384,6 +543,7 @@ const UNUSED_OPTIONAL_DEPENDENCY: Lint = Lint {
groups: &[],
default_level: LintLevel::Warn,
edition_lint_opts: None,
feature_gate: None,
};
pub fn unused_dependencies(

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

@ -982,3 +982,104 @@ error: `im_a_teapot` is specified
)
.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: verification of lint failed
--> Cargo.toml:9:1
|
9 | im-a-teapot = \"warn\"
| ^^^^^^^^^^^ use of unstable lint that has not been enabled
|
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: verification of lint failed
--> Cargo.toml:6:1
|
6 | im-a-teapot = { level = \"warn\", priority = 10 }
| ^^^^^^^^^^^ use of unstable lint that has not been enabled
|
note: `cargo::im_a_teapot` was inherited
--> foo/Cargo.toml:9:1
|
9 | workspace = true
| ---------
|
error: verification of lint failed
--> Cargo.toml:7:1
|
7 | test-dummy-unstable = { level = \"forbid\", priority = -1 }
| ^^^^^^^^^^^^^^^^^^^ use of unstable lint that has not been enabled
|
note: `cargo::test_dummy_unstable` was inherited
--> foo/Cargo.toml:9:1
|
9 | workspace = true
| ---------
|
error: encountered 2 errors(s) while verifying lints
",
)
.run();
}