diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs index 85b100c6ad..f48928aee8 100644 --- a/crates/ra_cfg/src/cfg_expr.rs +++ b/crates/ra_cfg/src/cfg_expr.rs @@ -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 { 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) -> Option { 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) -> Option { _ => 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) -> Option { }; // 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] diff --git a/crates/ra_cfg/src/lib.rs b/crates/ra_cfg/src/lib.rs index cd097f2a06..cd5a0a7b64 100644 --- a/crates/ra_cfg/src/lib.rs +++ b/crates/ra_cfg/src/lib.rs @@ -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 { - self.check(&parse_cfg(attr)) - } - pub fn insert_atom(&mut self, key: SmolStr) { self.atoms.insert(key); } diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs index e228e2145b..70ccd43052 100644 --- a/crates/ra_hir_def/src/attr.rs +++ b/crates/ra_hir_def/src/attr.rs @@ -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 + '_ { // 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)) } } diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 0994beec56..95a35a28db 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -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 }) } diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index b9c5424bf0..6ca53c6d89 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -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()); } } diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index 3183996247..03c41263aa 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs @@ -177,49 +177,35 @@ fn required_features(cfg_expr: &CfgExpr, features: &mut Vec) { 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::>(); + + 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)]"#, &[]); } }