Skip to content

Commit e6a64ea

Browse files
committed
Implement feature- and config-based lints
1 parent 54ffb7d commit e6a64ea

File tree

6 files changed

+130
-62
lines changed

6 files changed

+130
-62
lines changed

src/cargo/core/compiler/mod.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -809,9 +809,17 @@ fn build_base_args<'a, 'cfg>(
809809
cmd.args(args);
810810
}
811811

812-
unit.pkg.manifest().lints().set_flags(cmd);
813-
if let Some(virtual_lints) = bcx.ws.virtual_lints() {
814-
virtual_lints.set_flags(cmd);
812+
let unit_cfg = bcx.cfg(unit.kind);
813+
let features = bcx.resolve.features(unit.pkg.package_id());
814+
if let Some(ref lints) = unit.pkg.manifest().lints() {
815+
for ref lint_section in lints.iter() {
816+
lint_section.set_lint_flags(unit_cfg, features, cmd);
817+
}
818+
}
819+
if let Some(ref virtual_lints) = bcx.ws.virtual_lints() {
820+
for ref lint_section in virtual_lints.iter() {
821+
lint_section.set_lint_flags(unit_cfg, features, cmd);
822+
}
815823
}
816824

817825
// -C overflow-checks is implied by the setting of -C debug-assertions,

src/cargo/core/lints.rs

+37-14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use std::collections::BTreeMap;
2+
use std::collections::HashSet;
3+
use std::str::FromStr;
24

3-
use util::{CargoResult, ProcessBuilder};
5+
use util::{Cfg, CfgExpr, ProcessBuilder};
6+
use util::errors::CargoResult;
47

58
#[derive(Clone, PartialEq, Debug)]
69
enum LintKind {
@@ -34,30 +37,50 @@ impl LintKind {
3437
#[derive(Clone, Debug)]
3538
pub struct Lints {
3639
lints: Vec<(String, LintKind)>,
40+
cfg: Option<CfgExpr>,
3741
}
3842

3943
impl Lints {
4044
pub fn new(
41-
manifest_lints: Option<&BTreeMap<String, String>>,
45+
cfg: Option<&String>,
46+
manifest_lints: &BTreeMap<String, String>,
4247
warnings: &mut Vec<String>,
4348
) -> CargoResult<Lints> {
49+
let cfg = if let Some(t) = cfg {
50+
if t.starts_with("cfg(") && t.ends_with(')') {
51+
Some(CfgExpr::from_str(&t[4..t.len() - 1])?)
52+
} else {
53+
bail!("expected `cfg(...)`, found {}", t)
54+
}
55+
} else {
56+
None
57+
};
58+
4459
let mut lints = vec![];
45-
if let Some(lint_section) = manifest_lints {
46-
for (lint_name, lint_state) in lint_section.iter() {
47-
if let Some(state) = LintKind::try_from_string(lint_state) {
48-
lints.push((lint_name.to_string(), state));
49-
} else {
50-
warnings.push(format!(
51-
"invalid lint state for `{}` (expected `warn`, `allow`, `deny` or `forbid`)",
52-
lint_name
53-
));
54-
}
60+
for (lint_name, lint_state) in manifest_lints.iter() {
61+
if let Some(state) = LintKind::try_from_string(lint_state) {
62+
lints.push((lint_name.to_string(), state));
63+
} else {
64+
warnings.push(format!(
65+
"invalid lint state for `{}` (expected `warn`, `allow`, `deny` or `forbid`)",
66+
lint_name
67+
));
5568
}
5669
}
57-
Ok(Lints { lints })
70+
Ok(Lints { lints, cfg })
71+
}
72+
73+
pub fn set_lint_flags(&self, unit_cfg: &[Cfg], features: &HashSet<String>, cmd: &mut ProcessBuilder) {
74+
match self.cfg {
75+
None => self.set_flags(cmd),
76+
Some(CfgExpr::Value(Cfg::KeyPair(ref key, ref value)))
77+
if key == "feature" && features.contains(value) => self.set_flags(cmd),
78+
Some(ref cfg) if cfg.matches(unit_cfg) => self.set_flags(cmd),
79+
_ => (),
80+
}
5881
}
5982

60-
pub fn set_flags(&self, cmd: &mut ProcessBuilder) {
83+
fn set_flags(&self, cmd: &mut ProcessBuilder) {
6184
for (lint_name, state) in self.lints.iter() {
6285
cmd.arg(format!("-{}", state.flag())).arg(lint_name);
6386
}

src/cargo/core/manifest.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub struct Manifest {
4545
original: Rc<TomlManifest>,
4646
features: Features,
4747
edition: Edition,
48-
lints: Lints,
48+
lints: Option<Vec<Lints>>,
4949
im_a_teapot: Option<bool>,
5050
default_run: Option<String>,
5151
metabuild: Option<Vec<String>>,
@@ -70,7 +70,7 @@ pub struct VirtualManifest {
7070
workspace: WorkspaceConfig,
7171
profiles: Profiles,
7272
warnings: Warnings,
73-
lints: Lints,
73+
lints: Option<Vec<Lints>>,
7474
}
7575

7676
/// General metadata about a package which is just blindly uploaded to the
@@ -364,7 +364,7 @@ impl Manifest {
364364
workspace: WorkspaceConfig,
365365
features: Features,
366366
edition: Edition,
367-
lints: Lints,
367+
lints: Option<Vec<Lints>>,
368368
im_a_teapot: Option<bool>,
369369
default_run: Option<String>,
370370
original: Rc<TomlManifest>,
@@ -428,7 +428,7 @@ impl Manifest {
428428
pub fn warnings(&self) -> &Warnings {
429429
&self.warnings
430430
}
431-
pub fn lints(&self) -> &Lints {
431+
pub fn lints(&self) -> &Option<Vec<Lints>> {
432432
&self.lints
433433
}
434434
pub fn profiles(&self) -> &Profiles {
@@ -539,7 +539,7 @@ impl VirtualManifest {
539539
patch: HashMap<Url, Vec<Dependency>>,
540540
workspace: WorkspaceConfig,
541541
profiles: Profiles,
542-
lints: Lints,
542+
lints: Option<Vec<Lints>>,
543543
) -> VirtualManifest {
544544
VirtualManifest {
545545
replace,
@@ -563,7 +563,7 @@ impl VirtualManifest {
563563
&self.workspace
564564
}
565565

566-
pub fn lints(&self) -> &Lints {
566+
pub fn lints(&self) -> &Option<Vec<Lints>> {
567567
&self.lints
568568
}
569569

src/cargo/core/workspace.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -246,13 +246,13 @@ impl<'cfg> Workspace<'cfg> {
246246
}
247247
}
248248

249-
pub fn virtual_lints(&self) -> Option<&Lints> {
249+
pub fn virtual_lints(&self) -> &Option<Vec<Lints>> {
250250
let root = self.root_manifest
251251
.as_ref()
252252
.unwrap_or(&self.current_manifest);
253253
match *self.packages.get(root) {
254-
MaybePackage::Virtual(ref vm) => Some(vm.lints()),
255-
_ => None,
254+
MaybePackage::Virtual(ref vm) => vm.lints(),
255+
_ => &None,
256256
}
257257
}
258258

src/cargo/util/toml/mod.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ pub struct TomlManifest {
242242
workspace: Option<TomlWorkspace>,
243243
badges: Option<BTreeMap<String, BTreeMap<String, String>>>,
244244
lints: Option<BTreeMap<String, String>>,
245+
#[serde(rename = "lints2")]
246+
feature_lints: Option<BTreeMap<String, BTreeMap<String, String>>>,
245247
}
246248

247249
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
@@ -751,6 +753,7 @@ impl TomlManifest {
751753
badges: self.badges.clone(),
752754
cargo_features: self.cargo_features.clone(),
753755
lints: self.lints.clone(),
756+
feature_lints: self.feature_lints.clone(),
754757
});
755758

756759
fn map_deps(
@@ -1000,7 +1003,7 @@ impl TomlManifest {
10001003
),
10011004
};
10021005
let profiles = Profiles::new(me.profile.as_ref(), config, &features, &mut warnings)?;
1003-
let lints = Lints::new(me.lints.as_ref(), &mut warnings)?;
1006+
let lints = lints(me.lints.as_ref(), me.feature_lints.as_ref(), &mut warnings)?;
10041007
let publish = match project.publish {
10051008
Some(VecStringOrBool::VecString(ref vecstring)) => {
10061009
features
@@ -1114,7 +1117,7 @@ impl TomlManifest {
11141117
(me.replace(&mut cx)?, me.patch(&mut cx)?)
11151118
};
11161119
let profiles = Profiles::new(me.profile.as_ref(), config, &features, &mut warnings)?;
1117-
let lints = Lints::new(me.lints.as_ref(), &mut warnings)?;
1120+
let lints = lints(me.lints.as_ref(), me.feature_lints.as_ref(), &mut warnings)?;
11181121
let workspace_config = match me.workspace {
11191122
Some(ref config) => WorkspaceConfig::Root(WorkspaceRootConfig::new(
11201123
&root,
@@ -1495,3 +1498,24 @@ impl fmt::Debug for PathValue {
14951498
self.0.fmt(f)
14961499
}
14971500
}
1501+
1502+
fn lints(
1503+
toml_lints: Option<&BTreeMap<String, String>>,
1504+
toml_feature_lints: Option<&BTreeMap<String, BTreeMap<String, String>>>,
1505+
warnings: &mut Vec<String>
1506+
) -> CargoResult<Option<Vec<Lints>>> {
1507+
let mut lints = vec![];
1508+
if let Some(toml_lints) = toml_lints {
1509+
lints.push(Lints::new(None, toml_lints, warnings)?);
1510+
}
1511+
if let Some(toml_feature_lints) = toml_feature_lints {
1512+
for (ref cfg, ref feature_lints) in toml_feature_lints.iter() {
1513+
lints.push(Lints::new(Some(cfg), feature_lints, warnings)?);
1514+
}
1515+
}
1516+
Ok(if !lints.is_empty() {
1517+
Some(lints)
1518+
} else {
1519+
None
1520+
})
1521+
}

tests/testsuite/lints.rs

+47-34
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use support::{execs, project};
2-
use support::hamcrest::assert_that;
1+
use support::project;
32

43
#[test]
54
fn deny() {
@@ -19,12 +18,10 @@ fn deny() {
1918
.file("src/lib.rs", "fn foo() {}")
2019
.build();
2120

22-
assert_that(
23-
p.cargo("build"),
24-
execs()
25-
.with_status(101)
26-
.with_stderr_contains("[..]error: function is never used: `foo`[..]"),
27-
);
21+
p.cargo("build")
22+
.with_status(101)
23+
.with_stderr_contains("[..]error: function is never used: `foo`[..]")
24+
.run();
2825
}
2926

3027
#[test]
@@ -44,10 +41,7 @@ fn empty_lints_block() {
4441
.file("src/lib.rs", "fn foo() {}")
4542
.build();
4643

47-
assert_that(
48-
p.cargo("build"),
49-
execs().with_status(0),
50-
);
44+
p.cargo("build").with_status(0).run();
5145
}
5246

5347
#[test]
@@ -68,10 +62,7 @@ fn invalid_state() {
6862
.file("src/lib.rs", "fn foo() {}")
6963
.build();
7064

71-
assert_that(
72-
p.cargo("build"),
73-
execs().with_status(0),
74-
);
65+
p.cargo("build").with_status(0).run();
7566
}
7667

7768
#[test]
@@ -99,12 +90,10 @@ fn virtual_workspace() {
9990
.file("bar/src/lib.rs", "fn baz() {}")
10091
.build();
10192

102-
assert_that(
103-
p.cargo("build"),
104-
execs()
105-
.with_status(101)
106-
.with_stderr_contains("[..]error: function is never used: `baz`[..]"),
107-
);
93+
p.cargo("build")
94+
.with_status(101)
95+
.with_stderr_contains("[..]error: function is never used: `baz`[..]")
96+
.run();
10897
}
10998

11099
#[test]
@@ -132,12 +121,10 @@ fn member_workspace() {
132121
.file("bar/src/lib.rs", "fn baz() {}")
133122
.build();
134123

135-
assert_that(
136-
p.cargo("build"),
137-
execs()
138-
.with_status(101)
139-
.with_stderr_contains("[..]error: function is never used: `baz`[..]"),
140-
);
124+
p.cargo("build")
125+
.with_status(101)
126+
.with_stderr_contains("[..]error: function is never used: `baz`[..]")
127+
.run();
141128
}
142129

143130
#[test]
@@ -168,10 +155,36 @@ fn virtual_workspace_overrides() {
168155
.file("bar/src/lib.rs", "fn baz() {}")
169156
.build();
170157

171-
assert_that(
172-
p.cargo("build"),
173-
execs()
174-
.with_status(101)
175-
.with_stderr_contains("[..]error: function is never used: `baz`[..]"),
176-
);
158+
p.cargo("build")
159+
.with_status(101)
160+
.with_stderr_contains("[..]error: function is never used: `baz`[..]")
161+
.run();
162+
}
163+
164+
#[test]
165+
fn feature_flag() {
166+
let p = project()
167+
.file(
168+
"Cargo.toml",
169+
r#"
170+
[package]
171+
name = "foo"
172+
version = "0.0.1"
173+
authors = []
174+
175+
[features]
176+
bar = []
177+
178+
[lints2.'cfg(feature = "bar")']
179+
dead_code = "deny"
180+
"#,
181+
)
182+
.file("src/lib.rs", "fn foo() {}")
183+
.build();
184+
185+
p.cargo("build").with_status(0).run();
186+
p.cargo("build --features bar")
187+
.with_status(101)
188+
.with_stderr_contains("[..]error: function is never used: `foo`[..]")
189+
.run();
177190
}

0 commit comments

Comments
 (0)