wowlab_tidy/languages/toml/rules/cargo/
feature_no_std.rs1use super::{assignment_line, is_cargo_manifest};
2use crate::{Example, TomlCtx, Violation, violation};
3
4#[rustfmt::skip]
5const EXAMPLES: &[Example] = &[
6 Example { label: "additive std feature", code: "[features]\ndefault = [\"std\"]\nstd = []\n", pass: true },
7 Example { label: "no features table", code: "[package]\nname = \"foo\"\n", pass: true },
8 Example { label: "no-std feature", code: "[features]\nno-std = []\n", pass: false },
9 Example { label: "no_std feature", code: "[features]\nno_std = []\n", pass: false },
10 Example { label: "nostd feature", code: "[features]\nnostd = []\n", pass: false },
11];
12
13crate::toml_rule!(
14 toml_cargo_feature_no_std,
15 "Ban subtractive no-std Cargo features; provide an additive std feature instead.",
16 "Features must be additive so any combination compiles; a no-std feature that removes functionality breaks feature unification (M-FEATURES-ADDITIVE).",
17 Medium,
18);
19
20fn check_toml_cargo_feature_no_std(ctx: &TomlCtx<'_>) -> Vec<Violation> {
21 if !is_cargo_manifest(ctx.file.rel) || !ctx.parse.errors.is_empty() {
22 return Vec::new();
23 }
24
25 let Ok(document) = toml::from_str::<toml::Table>(ctx.file.contents) else {
26 return Vec::new();
27 };
28 let Some(features) = document.get("features").and_then(toml::Value::as_table) else {
29 return Vec::new();
30 };
31
32 features
33 .keys()
34 .filter(|name| matches!(name.as_str(), "no-std" | "no_std" | "nostd"))
35 .map(|name| {
36 violation(
37 ctx.file.rel,
38 assignment_line(ctx.file.lines, name),
39 format!(
40 "feature `{name}` is subtractive; invert it into an additive `std` feature"
41 ),
42 )
43 })
44 .collect()
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 fn run(source: &str) -> Vec<Violation> {
52 crate::test_support::check_source_toml_at(
53 "crates/foo/Cargo.toml",
54 source,
55 check_toml_cargo_feature_no_std,
56 )
57 }
58
59 crate::example_tests!(EXAMPLES, check_toml_cargo_feature_no_std);
60}