mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 20:43:21 +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 std::slice::Iter as SliceIter;
|
||||||
|
|
||||||
use ra_syntax::SmolStr;
|
use ra_syntax::SmolStr;
|
||||||
use tt::{Leaf, Subtree, TokenTree};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum CfgExpr {
|
pub enum CfgExpr {
|
||||||
|
@ -18,6 +17,9 @@ pub enum CfgExpr {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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.
|
/// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
|
||||||
pub fn fold(&self, query: &dyn Fn(&SmolStr, Option<&SmolStr>) -> bool) -> Option<bool> {
|
pub fn fold(&self, query: &dyn Fn(&SmolStr, Option<&SmolStr>) -> bool) -> Option<bool> {
|
||||||
match self {
|
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> {
|
fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
|
||||||
let name = match it.next() {
|
let name = match it.next() {
|
||||||
None => return None,
|
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),
|
Some(_) => return Some(CfgExpr::Invalid),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Peek
|
// Peek
|
||||||
let ret = match it.as_slice().first() {
|
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) {
|
match it.as_slice().get(1) {
|
||||||
Some(TokenTree::Leaf(Leaf::Literal(literal))) => {
|
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
|
||||||
it.next();
|
it.next();
|
||||||
it.next();
|
it.next();
|
||||||
// FIXME: escape? raw string?
|
// FIXME: escape? raw string?
|
||||||
|
@ -61,7 +59,7 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
|
||||||
_ => return Some(CfgExpr::Invalid),
|
_ => return Some(CfgExpr::Invalid),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(TokenTree::Subtree(subtree)) => {
|
Some(tt::TokenTree::Subtree(subtree)) => {
|
||||||
it.next();
|
it.next();
|
||||||
let mut sub_it = subtree.token_trees.iter();
|
let mut sub_it = subtree.token_trees.iter();
|
||||||
let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it)).collect();
|
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
|
// 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 == ',' {
|
if punct.char == ',' {
|
||||||
it.next();
|
it.next();
|
||||||
}
|
}
|
||||||
|
@ -99,7 +97,8 @@ mod tests {
|
||||||
|
|
||||||
fn assert_parse_result(input: &str, expected: CfgExpr) {
|
fn assert_parse_result(input: &str, expected: CfgExpr) {
|
||||||
let (tt, _) = get_token_tree_generated(input);
|
let (tt, _) = get_token_tree_generated(input);
|
||||||
assert_eq!(parse_cfg(&tt), expected);
|
let cfg = CfgExpr::parse(&tt);
|
||||||
|
assert_eq!(cfg, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -5,7 +5,7 @@ mod cfg_expr;
|
||||||
use ra_syntax::SmolStr;
|
use ra_syntax::SmolStr;
|
||||||
use rustc_hash::FxHashSet;
|
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.
|
/// 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
|
/// 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) {
|
pub fn insert_atom(&mut self, key: SmolStr) {
|
||||||
self.atoms.insert(key);
|
self.atoms.insert(key);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{ops, sync::Arc};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_expand::{hygiene::Hygiene, AstId, InFile};
|
use hir_expand::{hygiene::Hygiene, AstId, InFile};
|
||||||
use mbe::ast_to_token_tree;
|
use mbe::ast_to_token_tree;
|
||||||
use ra_cfg::CfgOptions;
|
use ra_cfg::{CfgExpr, CfgOptions};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, AstNode, AttrsOwner},
|
ast::{self, AstNode, AttrsOwner},
|
||||||
SmolStr,
|
SmolStr,
|
||||||
|
@ -125,9 +125,12 @@ impl Attrs {
|
||||||
AttrQuery { attrs: self, key }
|
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 :-)
|
// 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 attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
|
||||||
let cfg_exprs =
|
let cfg_exprs = attrs.cfg().collect();
|
||||||
attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
|
|
||||||
|
|
||||||
let nav = if let RunnableKind::DocTest { .. } = kind {
|
let nav = if let RunnableKind::DocTest { .. } = kind {
|
||||||
NavigationTarget::from_doc_commented(
|
NavigationTarget::from_doc_commented(
|
||||||
|
@ -242,9 +241,7 @@ 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 cfg_exprs =
|
let cfg_exprs = attrs.cfg().collect();
|
||||||
attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
|
|
||||||
|
|
||||||
let nav = module_def.to_nav(sema.db);
|
let nav = module_def.to_nav(sema.db);
|
||||||
Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs })
|
Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs })
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ use paths::{AbsPath, AbsPathBuf};
|
||||||
use ra_cfg::CfgOptions;
|
use ra_cfg::CfgOptions;
|
||||||
use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId};
|
use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
use stdx::split_delim;
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind},
|
cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind},
|
||||||
|
@ -544,11 +545,10 @@ fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions {
|
||||||
match rustc_cfgs {
|
match rustc_cfgs {
|
||||||
Ok(rustc_cfgs) => {
|
Ok(rustc_cfgs) => {
|
||||||
for line in rustc_cfgs.lines() {
|
for line in rustc_cfgs.lines() {
|
||||||
match line.find('=') {
|
match split_delim(line, '=') {
|
||||||
None => cfg_options.insert_atom(line.into()),
|
None => cfg_options.insert_atom(line.into()),
|
||||||
Some(pos) => {
|
Some((key, value)) => {
|
||||||
let key = &line[..pos];
|
let value = value.trim_matches('"');
|
||||||
let value = line[pos + 1..].trim_matches('"');
|
|
||||||
cfg_options.insert_key_value(key.into(), value.into());
|
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 {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use mbe::{ast_to_token_tree, TokenMap};
|
use mbe::ast_to_token_tree;
|
||||||
use ra_cfg::parse_cfg;
|
use ra_cfg::CfgExpr;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
SmolStr,
|
SmolStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
|
fn check(cfg: &str, expected_features: &[&str]) {
|
||||||
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
let cfg_expr = {
|
||||||
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
let source_file = ast::SourceFile::parse(cfg).ok().unwrap();
|
||||||
ast_to_token_tree(&tt).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]
|
#[test]
|
||||||
fn test_cfg_expr_minimal_features_needed() {
|
fn test_cfg_expr_minimal_features_needed() {
|
||||||
let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
|
check(r#"#![cfg(feature = "baz")]"#, &["baz"]);
|
||||||
let cfg_expr = parse_cfg(&subtree);
|
check(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#, &["baz", "foo"]);
|
||||||
let mut min_features = vec![];
|
check(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#, &["baz"]);
|
||||||
required_features(&cfg_expr, &mut min_features);
|
check(r#"#![cfg(foo)]"#, &[]);
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue