mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 12:33:33 +00:00
Cleanup CFG API
This commit is contained in:
parent
0cf8ee2dc2
commit
38e38d9b29
6 changed files with 44 additions and 63 deletions
|
@ -5,7 +5,6 @@
|
|||
use std::slice::Iter as SliceIter;
|
||||
|
||||
use ra_syntax::SmolStr;
|
||||
use tt::{Leaf, Subtree, TokenTree};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum CfgExpr {
|
||||
|
@ -18,6 +17,9 @@ pub enum CfgExpr {
|
|||
}
|
||||
|
||||
impl CfgExpr {
|
||||
pub fn parse(tt: &tt::Subtree) -> CfgExpr {
|
||||
next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid)
|
||||
}
|
||||
/// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
|
||||
pub fn fold(&self, query: &dyn Fn(&SmolStr, Option<&SmolStr>) -> bool) -> Option<bool> {
|
||||
match self {
|
||||
|
@ -35,22 +37,18 @@ impl CfgExpr {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_cfg(tt: &Subtree) -> CfgExpr {
|
||||
next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid)
|
||||
}
|
||||
|
||||
fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
|
||||
let name = match it.next() {
|
||||
None => return None,
|
||||
Some(TokenTree::Leaf(Leaf::Ident(ident))) => ident.text.clone(),
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(),
|
||||
Some(_) => return Some(CfgExpr::Invalid),
|
||||
};
|
||||
|
||||
// Peek
|
||||
let ret = match it.as_slice().first() {
|
||||
Some(TokenTree::Leaf(Leaf::Punct(punct))) if punct.char == '=' => {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => {
|
||||
match it.as_slice().get(1) {
|
||||
Some(TokenTree::Leaf(Leaf::Literal(literal))) => {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
|
||||
it.next();
|
||||
it.next();
|
||||
// FIXME: escape? raw string?
|
||||
|
@ -61,7 +59,7 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
|
|||
_ => return Some(CfgExpr::Invalid),
|
||||
}
|
||||
}
|
||||
Some(TokenTree::Subtree(subtree)) => {
|
||||
Some(tt::TokenTree::Subtree(subtree)) => {
|
||||
it.next();
|
||||
let mut sub_it = subtree.token_trees.iter();
|
||||
let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it)).collect();
|
||||
|
@ -76,7 +74,7 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
|
|||
};
|
||||
|
||||
// Eat comma separator
|
||||
if let Some(TokenTree::Leaf(Leaf::Punct(punct))) = it.as_slice().first() {
|
||||
if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = it.as_slice().first() {
|
||||
if punct.char == ',' {
|
||||
it.next();
|
||||
}
|
||||
|
@ -99,7 +97,8 @@ mod tests {
|
|||
|
||||
fn assert_parse_result(input: &str, expected: CfgExpr) {
|
||||
let (tt, _) = get_token_tree_generated(input);
|
||||
assert_eq!(parse_cfg(&tt), expected);
|
||||
let cfg = CfgExpr::parse(&tt);
|
||||
assert_eq!(cfg, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -5,7 +5,7 @@ mod cfg_expr;
|
|||
use ra_syntax::SmolStr;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
pub use cfg_expr::{parse_cfg, CfgExpr};
|
||||
pub use cfg_expr::CfgExpr;
|
||||
|
||||
/// Configuration options used for conditional compilition on items with `cfg` attributes.
|
||||
/// We have two kind of options in different namespaces: atomic options like `unix`, and
|
||||
|
@ -31,10 +31,6 @@ impl CfgOptions {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn is_cfg_enabled(&self, attr: &tt::Subtree) -> Option<bool> {
|
||||
self.check(&parse_cfg(attr))
|
||||
}
|
||||
|
||||
pub fn insert_atom(&mut self, key: SmolStr) {
|
||||
self.atoms.insert(key);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{ops, sync::Arc};
|
|||
use either::Either;
|
||||
use hir_expand::{hygiene::Hygiene, AstId, InFile};
|
||||
use mbe::ast_to_token_tree;
|
||||
use ra_cfg::CfgOptions;
|
||||
use ra_cfg::{CfgExpr, CfgOptions};
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, AttrsOwner},
|
||||
SmolStr,
|
||||
|
@ -125,9 +125,12 @@ impl Attrs {
|
|||
AttrQuery { attrs: self, key }
|
||||
}
|
||||
|
||||
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
|
||||
pub fn cfg(&self) -> impl Iterator<Item = CfgExpr> + '_ {
|
||||
// FIXME: handle cfg_attr :-)
|
||||
self.by_key("cfg").tt_values().all(|tt| cfg_options.is_cfg_enabled(tt) != Some(false))
|
||||
self.by_key("cfg").tt_values().map(CfgExpr::parse)
|
||||
}
|
||||
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
|
||||
self.cfg().all(|cfg| cfg_options.check(&cfg) != Some(false))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -168,8 +168,7 @@ fn runnable_fn(
|
|||
};
|
||||
|
||||
let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
|
||||
let cfg_exprs =
|
||||
attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
|
||||
let cfg_exprs = attrs.cfg().collect();
|
||||
|
||||
let nav = if let RunnableKind::DocTest { .. } = kind {
|
||||
NavigationTarget::from_doc_commented(
|
||||
|
@ -242,9 +241,7 @@ fn runnable_mod(
|
|||
.join("::");
|
||||
|
||||
let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module));
|
||||
let cfg_exprs =
|
||||
attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
|
||||
|
||||
let cfg_exprs = attrs.cfg().collect();
|
||||
let nav = module_def.to_nav(sema.db);
|
||||
Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs })
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ use paths::{AbsPath, AbsPathBuf};
|
|||
use ra_cfg::CfgOptions;
|
||||
use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use stdx::split_delim;
|
||||
|
||||
pub use crate::{
|
||||
cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind},
|
||||
|
@ -544,11 +545,10 @@ fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions {
|
|||
match rustc_cfgs {
|
||||
Ok(rustc_cfgs) => {
|
||||
for line in rustc_cfgs.lines() {
|
||||
match line.find('=') {
|
||||
match split_delim(line, '=') {
|
||||
None => cfg_options.insert_atom(line.into()),
|
||||
Some(pos) => {
|
||||
let key = &line[..pos];
|
||||
let value = line[pos + 1..].trim_matches('"');
|
||||
Some((key, value)) => {
|
||||
let value = value.trim_matches('"');
|
||||
cfg_options.insert_key_value(key.into(), value.into());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,49 +177,35 @@ fn required_features(cfg_expr: &CfgExpr, features: &mut Vec<String>) {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use mbe::{ast_to_token_tree, TokenMap};
|
||||
use ra_cfg::parse_cfg;
|
||||
use mbe::ast_to_token_tree;
|
||||
use ra_cfg::CfgExpr;
|
||||
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()
|
||||
fn check(cfg: &str, expected_features: &[&str]) {
|
||||
let cfg_expr = {
|
||||
let source_file = ast::SourceFile::parse(cfg).ok().unwrap();
|
||||
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
||||
let (tt, _) = ast_to_token_tree(&tt).unwrap();
|
||||
CfgExpr::parse(&tt)
|
||||
};
|
||||
|
||||
let mut features = vec![];
|
||||
required_features(&cfg_expr, &mut features);
|
||||
|
||||
let expected_features =
|
||||
expected_features.iter().map(|&it| SmolStr::new(it)).collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(features, expected_features);
|
||||
}
|
||||
|
||||
#[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![];
|
||||
required_features(&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![];
|
||||
required_features(&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![];
|
||||
required_features(&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![];
|
||||
required_features(&cfg_expr, &mut min_features);
|
||||
assert!(min_features.is_empty());
|
||||
check(r#"#![cfg(feature = "baz")]"#, &["baz"]);
|
||||
check(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#, &["baz", "foo"]);
|
||||
check(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#, &["baz"]);
|
||||
check(r#"#![cfg(foo)]"#, &[]);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue