mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 07:04:22 +00:00
add support of feature flag for runnables #4464
Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
This commit is contained in:
parent
43339058e3
commit
48d7c61e26
6 changed files with 145 additions and 99 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -1387,17 +1387,20 @@ dependencies = [
|
||||||
"lsp-types",
|
"lsp-types",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pico-args",
|
"pico-args",
|
||||||
|
"ra_cfg",
|
||||||
"ra_db",
|
"ra_db",
|
||||||
"ra_flycheck",
|
"ra_flycheck",
|
||||||
"ra_hir",
|
"ra_hir",
|
||||||
"ra_hir_def",
|
"ra_hir_def",
|
||||||
"ra_hir_ty",
|
"ra_hir_ty",
|
||||||
"ra_ide",
|
"ra_ide",
|
||||||
|
"ra_mbe",
|
||||||
"ra_proc_macro_srv",
|
"ra_proc_macro_srv",
|
||||||
"ra_prof",
|
"ra_prof",
|
||||||
"ra_project_model",
|
"ra_project_model",
|
||||||
"ra_syntax",
|
"ra_syntax",
|
||||||
"ra_text_edit",
|
"ra_text_edit",
|
||||||
|
"ra_tt",
|
||||||
"ra_vfs",
|
"ra_vfs",
|
||||||
"rand",
|
"rand",
|
||||||
"relative-path",
|
"relative-path",
|
||||||
|
|
|
@ -33,33 +33,6 @@ impl CfgExpr {
|
||||||
CfgExpr::Not(pred) => pred.fold(query).map(|s| !s),
|
CfgExpr::Not(pred) => pred.fold(query).map(|s| !s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return minimal features needed
|
|
||||||
pub fn minimal_features_needed(&self) -> Vec<SmolStr> {
|
|
||||||
let mut features = vec![];
|
|
||||||
self.collect_minimal_features_needed(&mut features);
|
|
||||||
|
|
||||||
features
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect_minimal_features_needed(&self, features: &mut Vec<SmolStr>) {
|
|
||||||
match self {
|
|
||||||
CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
|
|
||||||
CfgExpr::All(preds) => {
|
|
||||||
preds.iter().for_each(|cfg| cfg.collect_minimal_features_needed(features));
|
|
||||||
}
|
|
||||||
CfgExpr::Any(preds) => {
|
|
||||||
for cfg in preds {
|
|
||||||
let len_features = features.len();
|
|
||||||
cfg.collect_minimal_features_needed(features);
|
|
||||||
if len_features != features.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_cfg(tt: &Subtree) -> CfgExpr {
|
pub fn parse_cfg(tt: &Subtree) -> CfgExpr {
|
||||||
|
@ -160,32 +133,4 @@ mod tests {
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cfg_expr_minimal_features_needed() {
|
|
||||||
let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
|
|
||||||
let cfg_expr = parse_cfg(&subtree);
|
|
||||||
|
|
||||||
assert_eq!(cfg_expr.minimal_features_needed(), vec![SmolStr::new("baz")]);
|
|
||||||
|
|
||||||
let (subtree, _) =
|
|
||||||
get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
|
|
||||||
let cfg_expr = parse_cfg(&subtree);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
cfg_expr.minimal_features_needed(),
|
|
||||||
vec![SmolStr::new("baz"), SmolStr::new("foo")]
|
|
||||||
);
|
|
||||||
|
|
||||||
let (subtree, _) =
|
|
||||||
get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
|
|
||||||
let cfg_expr = parse_cfg(&subtree);
|
|
||||||
|
|
||||||
assert_eq!(cfg_expr.minimal_features_needed(), vec![SmolStr::new("baz")]);
|
|
||||||
|
|
||||||
let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
|
|
||||||
let cfg_expr = parse_cfg(&subtree);
|
|
||||||
|
|
||||||
assert!(cfg_expr.minimal_features_needed().is_empty());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,13 +10,14 @@ use ra_syntax::{
|
||||||
|
|
||||||
use crate::FileId;
|
use crate::FileId;
|
||||||
use ast::DocCommentsOwner;
|
use ast::DocCommentsOwner;
|
||||||
|
use ra_cfg::CfgExpr;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Runnable {
|
pub struct Runnable {
|
||||||
pub range: TextRange,
|
pub range: TextRange,
|
||||||
pub kind: RunnableKind,
|
pub kind: RunnableKind,
|
||||||
pub features_needed: Option<Vec<SmolStr>>,
|
pub cfg_exprs: Vec<CfgExpr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -118,9 +119,10 @@ fn runnable_fn(
|
||||||
};
|
};
|
||||||
|
|
||||||
let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
|
let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
|
||||||
let features_needed = get_features_needed(attrs);
|
let cfg_exprs =
|
||||||
|
attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
|
||||||
|
|
||||||
Some(Runnable { range: fn_def.syntax().text_range(), kind, features_needed })
|
Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -183,15 +185,10 @@ fn runnable_mod(
|
||||||
.join("::");
|
.join("::");
|
||||||
|
|
||||||
let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module));
|
let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module));
|
||||||
let features_needed = get_features_needed(attrs);
|
let cfg_exprs =
|
||||||
|
attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
|
||||||
|
|
||||||
Some(Runnable { range, kind: RunnableKind::TestMod { path }, features_needed })
|
Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs })
|
||||||
}
|
|
||||||
|
|
||||||
fn get_features_needed(attrs: Attrs) -> Option<Vec<SmolStr>> {
|
|
||||||
let cfg_expr = attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree));
|
|
||||||
let features_needed = cfg_expr.map(|cfg| cfg.minimal_features_needed()).flatten().collect();
|
|
||||||
Some(features_needed).filter(|it: &Vec<SmolStr>| !it.is_empty())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -223,7 +220,7 @@ mod tests {
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 1..21,
|
range: 1..21,
|
||||||
kind: Bin,
|
kind: Bin,
|
||||||
features_needed: None,
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 22..46,
|
range: 22..46,
|
||||||
|
@ -235,7 +232,7 @@ mod tests {
|
||||||
ignore: false,
|
ignore: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
features_needed: None,
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 47..81,
|
range: 47..81,
|
||||||
|
@ -247,7 +244,7 @@ mod tests {
|
||||||
ignore: true,
|
ignore: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
features_needed: None,
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
"###
|
"###
|
||||||
|
@ -275,7 +272,7 @@ mod tests {
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 1..21,
|
range: 1..21,
|
||||||
kind: Bin,
|
kind: Bin,
|
||||||
features_needed: None,
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 22..64,
|
range: 22..64,
|
||||||
|
@ -284,7 +281,7 @@ mod tests {
|
||||||
"foo",
|
"foo",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
features_needed: None,
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
"###
|
"###
|
||||||
|
@ -315,7 +312,7 @@ mod tests {
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 1..21,
|
range: 1..21,
|
||||||
kind: Bin,
|
kind: Bin,
|
||||||
features_needed: None,
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 51..105,
|
range: 51..105,
|
||||||
|
@ -324,7 +321,7 @@ mod tests {
|
||||||
"Data::foo",
|
"Data::foo",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
features_needed: None,
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
"###
|
"###
|
||||||
|
@ -352,7 +349,7 @@ mod tests {
|
||||||
kind: TestMod {
|
kind: TestMod {
|
||||||
path: "test_mod",
|
path: "test_mod",
|
||||||
},
|
},
|
||||||
features_needed: None,
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 28..57,
|
range: 28..57,
|
||||||
|
@ -364,7 +361,7 @@ mod tests {
|
||||||
ignore: false,
|
ignore: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
features_needed: None,
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
"###
|
"###
|
||||||
|
@ -394,7 +391,7 @@ mod tests {
|
||||||
kind: TestMod {
|
kind: TestMod {
|
||||||
path: "foo::test_mod",
|
path: "foo::test_mod",
|
||||||
},
|
},
|
||||||
features_needed: None,
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 46..79,
|
range: 46..79,
|
||||||
|
@ -406,7 +403,7 @@ mod tests {
|
||||||
ignore: false,
|
ignore: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
features_needed: None,
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
"###
|
"###
|
||||||
|
@ -438,7 +435,7 @@ mod tests {
|
||||||
kind: TestMod {
|
kind: TestMod {
|
||||||
path: "foo::bar::test_mod",
|
path: "foo::bar::test_mod",
|
||||||
},
|
},
|
||||||
features_needed: None,
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 68..105,
|
range: 68..105,
|
||||||
|
@ -450,7 +447,7 @@ mod tests {
|
||||||
ignore: false,
|
ignore: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
features_needed: None,
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
"###
|
"###
|
||||||
|
@ -482,11 +479,12 @@ mod tests {
|
||||||
ignore: false,
|
ignore: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
features_needed: Some(
|
cfg_exprs: [
|
||||||
[
|
KeyValue {
|
||||||
"foo",
|
key: "feature",
|
||||||
],
|
value: "foo",
|
||||||
),
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
"###
|
"###
|
||||||
|
@ -518,12 +516,20 @@ mod tests {
|
||||||
ignore: false,
|
ignore: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
features_needed: Some(
|
cfg_exprs: [
|
||||||
[
|
All(
|
||||||
"foo",
|
[
|
||||||
"bar",
|
KeyValue {
|
||||||
],
|
key: "feature",
|
||||||
),
|
value: "foo",
|
||||||
|
},
|
||||||
|
KeyValue {
|
||||||
|
key: "feature",
|
||||||
|
value: "bar",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
"###
|
"###
|
||||||
|
|
|
@ -40,6 +40,7 @@ ra_project_model = { path = "../ra_project_model" }
|
||||||
ra_syntax = { path = "../ra_syntax" }
|
ra_syntax = { path = "../ra_syntax" }
|
||||||
ra_text_edit = { path = "../ra_text_edit" }
|
ra_text_edit = { path = "../ra_text_edit" }
|
||||||
ra_vfs = "0.6.0"
|
ra_vfs = "0.6.0"
|
||||||
|
ra_cfg = { path = "../ra_cfg"}
|
||||||
|
|
||||||
# This should only be used in CLI
|
# This should only be used in CLI
|
||||||
ra_db = { path = "../ra_db" }
|
ra_db = { path = "../ra_db" }
|
||||||
|
@ -55,6 +56,8 @@ winapi = "0.3.8"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
insta = "0.16.0"
|
insta = "0.16.0"
|
||||||
test_utils = { path = "../test_utils" }
|
test_utils = { path = "../test_utils" }
|
||||||
|
mbe = { path = "../ra_mbe", package = "ra_mbe" }
|
||||||
|
tt = { path = "../ra_tt", package = "ra_tt" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
jemalloc = [ "ra_prof/jemalloc" ]
|
jemalloc = [ "ra_prof/jemalloc" ]
|
||||||
|
|
|
@ -21,7 +21,7 @@ impl CargoTargetSpec {
|
||||||
pub(crate) fn runnable_args(
|
pub(crate) fn runnable_args(
|
||||||
spec: Option<CargoTargetSpec>,
|
spec: Option<CargoTargetSpec>,
|
||||||
kind: &RunnableKind,
|
kind: &RunnableKind,
|
||||||
features_needed: &Option<Vec<SmolStr>>,
|
features_needed: &Vec<SmolStr>,
|
||||||
) -> Result<(Vec<String>, Vec<String>)> {
|
) -> Result<(Vec<String>, Vec<String>)> {
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
let mut extra_args = Vec::new();
|
let mut extra_args = Vec::new();
|
||||||
|
@ -76,12 +76,11 @@ impl CargoTargetSpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(features_needed) = features_needed {
|
features_needed.iter().for_each(|feature| {
|
||||||
features_needed.iter().for_each(|feature| {
|
args.push("--features".to_string());
|
||||||
args.push("--features".to_string());
|
args.push(feature.to_string());
|
||||||
args.push(feature.to_string());
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok((args, extra_args))
|
Ok((args, extra_args))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,12 +17,13 @@ use lsp_types::{
|
||||||
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
|
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
|
||||||
SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, Url, WorkspaceEdit,
|
SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, Url, WorkspaceEdit,
|
||||||
};
|
};
|
||||||
|
use ra_cfg::CfgExpr;
|
||||||
use ra_ide::{
|
use ra_ide::{
|
||||||
Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
|
Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
|
||||||
};
|
};
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use ra_project_model::TargetKind;
|
use ra_project_model::TargetKind;
|
||||||
use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize};
|
use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::to_value;
|
use serde_json::to_value;
|
||||||
|
@ -38,6 +39,7 @@ use crate::{
|
||||||
world::WorldSnapshot,
|
world::WorldSnapshot,
|
||||||
LspError, Result,
|
LspError, Result,
|
||||||
};
|
};
|
||||||
|
use hir::Attrs;
|
||||||
|
|
||||||
pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> {
|
pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> {
|
||||||
let _p = profile("handle_analyzer_status");
|
let _p = profile("handle_analyzer_status");
|
||||||
|
@ -1006,8 +1008,12 @@ fn to_lsp_runnable(
|
||||||
) -> Result<lsp_ext::Runnable> {
|
) -> Result<lsp_ext::Runnable> {
|
||||||
let spec = CargoTargetSpec::for_file(world, file_id)?;
|
let spec = CargoTargetSpec::for_file(world, file_id)?;
|
||||||
let target = spec.as_ref().map(|s| s.target.clone());
|
let target = spec.as_ref().map(|s| s.target.clone());
|
||||||
|
let mut features_needed = vec![];
|
||||||
|
for cfg_expr in &runnable.cfg_exprs {
|
||||||
|
collect_minimal_features_needed(cfg_expr, &mut features_needed);
|
||||||
|
}
|
||||||
let (args, extra_args) =
|
let (args, extra_args) =
|
||||||
CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.features_needed)?;
|
CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?;
|
||||||
let line_index = world.analysis().file_line_index(file_id)?;
|
let line_index = world.analysis().file_line_index(file_id)?;
|
||||||
let label = match &runnable.kind {
|
let label = match &runnable.kind {
|
||||||
RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
|
RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
|
||||||
|
@ -1033,6 +1039,39 @@ fn to_lsp_runnable(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_features_needed(attrs: Attrs) -> Option<Vec<SmolStr>> {
|
||||||
|
let cfg_expr = attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree));
|
||||||
|
let features_needed = cfg_expr
|
||||||
|
.map(|cfg| {
|
||||||
|
let mut min_features = vec![];
|
||||||
|
collect_minimal_features_needed(&cfg, &mut min_features);
|
||||||
|
min_features
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
Some(features_needed).filter(|it: &Vec<SmolStr>| !it.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fill minimal features needed
|
||||||
|
fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec<SmolStr>) {
|
||||||
|
match cfg_expr {
|
||||||
|
CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
|
||||||
|
CfgExpr::All(preds) => {
|
||||||
|
preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features));
|
||||||
|
}
|
||||||
|
CfgExpr::Any(preds) => {
|
||||||
|
for cfg in preds {
|
||||||
|
let len_features = features.len();
|
||||||
|
collect_minimal_features_needed(cfg, features);
|
||||||
|
if len_features != features.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_inlay_hints(
|
pub fn handle_inlay_hints(
|
||||||
world: WorldSnapshot,
|
world: WorldSnapshot,
|
||||||
params: InlayHintsParams,
|
params: InlayHintsParams,
|
||||||
|
@ -1169,3 +1208,54 @@ pub fn handle_semantic_tokens_range(
|
||||||
let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
|
let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
|
||||||
Ok(Some(semantic_tokens.into()))
|
Ok(Some(semantic_tokens.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use mbe::{ast_to_token_tree, TokenMap};
|
||||||
|
use ra_cfg::parse_cfg;
|
||||||
|
use ra_syntax::{
|
||||||
|
ast::{self, AstNode},
|
||||||
|
SmolStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
|
||||||
|
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
||||||
|
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
||||||
|
ast_to_token_tree(&tt).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cfg_expr_minimal_features_needed() {
|
||||||
|
let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
|
||||||
|
let cfg_expr = parse_cfg(&subtree);
|
||||||
|
let mut min_features = vec![];
|
||||||
|
collect_minimal_features_needed(&cfg_expr, &mut min_features);
|
||||||
|
|
||||||
|
assert_eq!(min_features, vec![SmolStr::new("baz")]);
|
||||||
|
|
||||||
|
let (subtree, _) =
|
||||||
|
get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
|
||||||
|
let cfg_expr = parse_cfg(&subtree);
|
||||||
|
|
||||||
|
let mut min_features = vec![];
|
||||||
|
collect_minimal_features_needed(&cfg_expr, &mut min_features);
|
||||||
|
assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]);
|
||||||
|
|
||||||
|
let (subtree, _) =
|
||||||
|
get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
|
||||||
|
let cfg_expr = parse_cfg(&subtree);
|
||||||
|
|
||||||
|
let mut min_features = vec![];
|
||||||
|
collect_minimal_features_needed(&cfg_expr, &mut min_features);
|
||||||
|
assert_eq!(min_features, vec![SmolStr::new("baz")]);
|
||||||
|
|
||||||
|
let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
|
||||||
|
let cfg_expr = parse_cfg(&subtree);
|
||||||
|
|
||||||
|
let mut min_features = vec![];
|
||||||
|
collect_minimal_features_needed(&cfg_expr, &mut min_features);
|
||||||
|
assert!(min_features.is_empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue