From ffe179a73663b111e4b3ee8a3f525fb3e461c78e Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Mon, 30 Sep 2019 02:50:56 +0800 Subject: Pass attributes as token tree to DefCollector --- crates/ra_hir/src/nameres/collector.rs | 19 ++++++------- crates/ra_hir/src/nameres/raw.rs | 52 +++++++++++++++++++++++++++------- crates/ra_syntax/src/ast/generated.rs | 1 + crates/ra_syntax/src/grammar.ron | 3 +- 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index a568fdabd..40e56dfe0 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -523,7 +523,7 @@ where // `#[macro_use] extern crate` is hoisted to imports macros before collecting // any other items. for item in items { - if let raw::RawItem::Import(import_id) = *item { + if let raw::RawItemKind::Import(import_id) = item.kind { let import = self.raw_items[import_id].clone(); if import.is_extern_crate && import.is_macro_use { self.def_collector.import_macros_from_extern_crate(self.module_id, &import); @@ -532,15 +532,14 @@ where } for item in items { - match *item { - raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]), - raw::RawItem::Import(import_id) => self.def_collector.unresolved_imports.push(( - self.module_id, - import_id, - self.raw_items[import_id].clone(), - )), - raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]), - raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]), + match item.kind { + raw::RawItemKind::Module(m) => self.collect_module(&self.raw_items[m]), + raw::RawItemKind::Import(import_id) => self + .def_collector + .unresolved_imports + .push((self.module_id, import_id, self.raw_items[import_id].clone())), + raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]), + raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]), } } } diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 606bd1a95..cacbcb517 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -2,6 +2,7 @@ use std::{ops::Index, sync::Arc}; +use mbe::ast_to_token_tree; use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId}; use ra_syntax::{ ast::{self, AttrsOwner, NameOwner}, @@ -28,6 +29,8 @@ pub struct RawItems { items: Vec, } +type Attrs = Arc<[tt::Subtree]>; + #[derive(Debug, Default, PartialEq, Eq)] pub struct ImportSourceMap { map: ArenaMap, @@ -119,8 +122,14 @@ impl Index for RawItems { } } +#[derive(Debug, PartialEq, Eq, Clone)] +pub(super) struct RawItem { + pub(super) attrs: Attrs, + pub(super) kind: RawItemKind, +} + #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub(super) enum RawItem { +pub(super) enum RawItemKind { Module(Module), Import(ImportId), Def(Def), @@ -215,6 +224,7 @@ impl RawItemsCollector<&DB> { } fn add_item(&mut self, current_module: Option, item: ast::ModuleItem) { + let attrs = self.parse_attrs(&item); let (kind, name) = match item { ast::ModuleItem::Module(module) => { self.add_module(current_module, module); @@ -263,7 +273,7 @@ impl RawItemsCollector<&DB> { if let Some(name) = name { let name = name.as_name(); let def = self.raw_items.defs.alloc(DefData { name, kind }); - self.push_item(current_module, RawItem::Def(def)) + self.push_item(current_module, attrs, RawItemKind::Def(def)); } } @@ -272,6 +282,7 @@ impl RawItemsCollector<&DB> { Some(it) => it.as_name(), None => return, }; + let attrs = self.parse_attrs(&module); let ast_id = self.source_ast_id_map.ast_id(&module); let is_macro_use = module.has_atom_attr("macro_use"); @@ -283,7 +294,7 @@ impl RawItemsCollector<&DB> { attr_path, is_macro_use, }); - self.push_item(current_module, RawItem::Module(item)); + self.push_item(current_module, attrs, RawItemKind::Module(item)); return; } @@ -297,7 +308,7 @@ impl RawItemsCollector<&DB> { is_macro_use, }); self.process_module(Some(item), item_list); - self.push_item(current_module, RawItem::Module(item)); + self.push_item(current_module, attrs, RawItemKind::Module(item)); return; } tested_by!(name_res_works_for_broken_modules); @@ -305,6 +316,7 @@ impl RawItemsCollector<&DB> { fn add_use_item(&mut self, current_module: Option, use_item: ast::UseItem) { let is_prelude = use_item.has_atom_attr("prelude_import"); + let attrs = self.parse_attrs(&use_item); Path::expand_use_item( Source { ast: use_item, file_id: self.file_id }, @@ -318,7 +330,12 @@ impl RawItemsCollector<&DB> { is_extern_crate: false, is_macro_use: false, }; - self.push_import(current_module, import_data, Either::A(AstPtr::new(use_tree))); + self.push_import( + current_module, + attrs.clone(), + import_data, + Either::A(AstPtr::new(use_tree)), + ); }, ) } @@ -331,6 +348,7 @@ impl RawItemsCollector<&DB> { if let Some(name_ref) = extern_crate.name_ref() { let path = Path::from_name_ref(&name_ref); let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); + let attrs = self.parse_attrs(&extern_crate); let is_macro_use = extern_crate.has_atom_attr("macro_use"); let import_data = ImportData { path, @@ -340,7 +358,12 @@ impl RawItemsCollector<&DB> { is_extern_crate: true, is_macro_use, }; - self.push_import(current_module, import_data, Either::B(AstPtr::new(&extern_crate))); + self.push_import( + current_module, + attrs, + import_data, + Either::B(AstPtr::new(&extern_crate)), + ); } } @@ -358,21 +381,22 @@ impl RawItemsCollector<&DB> { let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export"); let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); - self.push_item(current_module, RawItem::Macro(m)); + self.push_item(current_module, attrs, RawItemKind::Macro(m)); } fn push_import( &mut self, current_module: Option, + attrs: Attrs, data: ImportData, source: ImportSourcePtr, ) { let import = self.raw_items.imports.alloc(data); self.source_map.insert(import, source); - self.push_item(current_module, RawItem::Import(import)) + self.push_item(current_module, attrs, RawItemKind::Import(import)) } - fn push_item(&mut self, current_module: Option, item: RawItem) { + fn push_item(&mut self, current_module: Option, attrs: Attrs, kind: RawItemKind) { match current_module { Some(module) => match &mut self.raw_items.modules[module] { ModuleData::Definition { items, .. } => items, @@ -380,7 +404,15 @@ impl RawItemsCollector<&DB> { }, None => &mut self.raw_items.items, } - .push(item) + .push(RawItem { attrs, kind }) + } + + fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs { + item.attrs() + .flat_map(|attr| attr.value()) + .flat_map(|tt| ast_to_token_tree(&tt)) + .map(|(tt, _)| tt) + .collect() } } diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 3bb5571ee..34b22c3e2 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -1962,6 +1962,7 @@ impl AstNode for ModuleItem { } } } +impl ast::AttrsOwner for ModuleItem {} impl ModuleItem {} #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Name { diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 30328f59f..5f7d8c3bf 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -397,7 +397,8 @@ Grammar( ), "ModuleItem": ( enum: ["StructDef", "EnumDef", "FnDef", "TraitDef", "TypeAliasDef", "ImplBlock", - "UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ] + "UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ], + traits: ["AttrsOwner"] ), "ImplItem": ( enum: ["FnDef", "TypeAliasDef", "ConstDef"] -- cgit v1.2.3 From b1ed887d813bf5775a16624694939fdf836f97b1 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Mon, 30 Sep 2019 06:52:15 +0800 Subject: Introduce ra_cfg to parse and evaluate CfgExpr --- Cargo.lock | 14 ++++ crates/ra_cfg/Cargo.toml | 14 ++++ crates/ra_cfg/src/cfg_expr.rs | 128 +++++++++++++++++++++++++++++++++ crates/ra_cfg/src/lib.rs | 43 +++++++++++ crates/ra_db/Cargo.toml | 1 + crates/ra_db/src/input.rs | 9 ++- crates/ra_hir/Cargo.toml | 1 + crates/ra_hir/src/attr.rs | 58 +++++++++++++++ crates/ra_hir/src/lib.rs | 1 + crates/ra_hir/src/nameres/collector.rs | 52 +++++++++----- crates/ra_hir/src/nameres/raw.rs | 21 +++--- 11 files changed, 315 insertions(+), 27 deletions(-) create mode 100644 crates/ra_cfg/Cargo.toml create mode 100644 crates/ra_cfg/src/cfg_expr.rs create mode 100644 crates/ra_cfg/src/lib.rs create mode 100644 crates/ra_hir/src/attr.rs diff --git a/Cargo.lock b/Cargo.lock index 988f7ec0b..b95f176fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -922,6 +922,16 @@ dependencies = [ "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ra_cfg" +version = "0.1.0" +dependencies = [ + "ra_mbe 0.1.0", + "ra_syntax 0.1.0", + "ra_tt 0.1.0", + "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ra_cli" version = "0.1.0" @@ -941,6 +951,7 @@ dependencies = [ name = "ra_db" version = "0.1.0" dependencies = [ + "ra_cfg 0.1.0", "ra_prof 0.1.0", "ra_syntax 0.1.0", "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -971,6 +982,7 @@ dependencies = [ "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "ra_arena 0.1.0", + "ra_cfg 0.1.0", "ra_db 0.1.0", "ra_mbe 0.1.0", "ra_prof 0.1.0", @@ -993,6 +1005,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "proptest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", "ra_assists 0.1.0", + "ra_cfg 0.1.0", "ra_db 0.1.0", "ra_fmt 0.1.0", "ra_hir 0.1.0", @@ -1075,6 +1088,7 @@ dependencies = [ "cargo_metadata 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "ra_arena 0.1.0", + "ra_cfg 0.1.0", "ra_db 0.1.0", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/crates/ra_cfg/Cargo.toml b/crates/ra_cfg/Cargo.toml new file mode 100644 index 000000000..b28affc3a --- /dev/null +++ b/crates/ra_cfg/Cargo.toml @@ -0,0 +1,14 @@ +[package] +edition = "2018" +name = "ra_cfg" +version = "0.1.0" +authors = ["rust-analyzer developers"] + +[dependencies] +rustc-hash = "1.0.1" + +ra_syntax = { path = "../ra_syntax" } +tt = { path = "../ra_tt", package = "ra_tt" } + +[dev-dependencies] +mbe = { path = "../ra_mbe", package = "ra_mbe" } diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs new file mode 100644 index 000000000..efeadf462 --- /dev/null +++ b/crates/ra_cfg/src/cfg_expr.rs @@ -0,0 +1,128 @@ +use std::slice::Iter as SliceIter; + +use ra_syntax::SmolStr; +use tt::{Leaf, Subtree, TokenTree}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CfgExpr { + Invalid, + Atom(SmolStr), + KeyValue { key: SmolStr, value: SmolStr }, + All(Vec), + Any(Vec), + Not(Box), +} + +impl CfgExpr { + /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. + pub fn fold(&self, query: &impl Fn(&SmolStr, Option<&SmolStr>) -> bool) -> Option { + match self { + CfgExpr::Invalid => None, + CfgExpr::Atom(name) => Some(query(name, None)), + CfgExpr::KeyValue { key, value } => Some(query(key, Some(value))), + CfgExpr::All(preds) => { + preds.iter().try_fold(true, |s, pred| Some(s && pred.fold(query)?)) + } + CfgExpr::Any(preds) => { + preds.iter().try_fold(false, |s, pred| Some(s || pred.fold(query)?)) + } + CfgExpr::Not(pred) => pred.fold(query).map(|s| !s), + } + } +} + +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(_) => return Some(CfgExpr::Invalid), + }; + + // Peek + let ret = match it.as_slice().first() { + Some(TokenTree::Leaf(Leaf::Punct(punct))) if punct.char == '=' => { + match it.as_slice().get(1) { + Some(TokenTree::Leaf(Leaf::Literal(literal))) => { + it.next(); + it.next(); + // FIXME: escape? raw string? + let value = + SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"')); + CfgExpr::KeyValue { key: name, value } + } + _ => return Some(CfgExpr::Invalid), + } + } + Some(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(); + match name.as_str() { + "all" => CfgExpr::All(subs), + "any" => CfgExpr::Any(subs), + "not" => CfgExpr::Not(Box::new(subs.pop().unwrap_or(CfgExpr::Invalid))), + _ => CfgExpr::Invalid, + } + } + _ => CfgExpr::Atom(name), + }; + + // Eat comma separator + if let Some(TokenTree::Leaf(Leaf::Punct(punct))) = it.as_slice().first() { + if punct.char == ',' { + it.next(); + } + } + Some(ret) +} + +#[cfg(test)] +mod tests { + use super::*; + + use mbe::ast_to_token_tree; + use ra_syntax::ast::{self, AstNode}; + + fn assert_parse_result(input: &str, expected: CfgExpr) { + let source_file = ast::SourceFile::parse(input).ok().unwrap(); + let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); + let (tt, _) = ast_to_token_tree(&tt).unwrap(); + assert_eq!(parse_cfg(&tt), expected); + } + + #[test] + fn test_cfg_expr_parser() { + assert_parse_result("#![cfg(foo)]", CfgExpr::Atom("foo".into())); + assert_parse_result("#![cfg(foo,)]", CfgExpr::Atom("foo".into())); + assert_parse_result( + "#![cfg(not(foo))]", + CfgExpr::Not(Box::new(CfgExpr::Atom("foo".into()))), + ); + assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid); + + // Only take the first + assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgExpr::Atom("foo".into())); + + assert_parse_result( + r#"#![cfg(all(foo, bar = "baz"))]"#, + CfgExpr::All(vec![ + CfgExpr::Atom("foo".into()), + CfgExpr::KeyValue { key: "bar".into(), value: "baz".into() }, + ]), + ); + + assert_parse_result( + r#"#![cfg(any(not(), all(), , bar = "baz",))]"#, + CfgExpr::Any(vec![ + CfgExpr::Not(Box::new(CfgExpr::Invalid)), + CfgExpr::All(vec![]), + CfgExpr::Invalid, + CfgExpr::KeyValue { key: "bar".into(), value: "baz".into() }, + ]), + ); + } +} diff --git a/crates/ra_cfg/src/lib.rs b/crates/ra_cfg/src/lib.rs new file mode 100644 index 000000000..fa5822d8a --- /dev/null +++ b/crates/ra_cfg/src/lib.rs @@ -0,0 +1,43 @@ +//! ra_cfg defines conditional compiling options, `cfg` attibute parser and evaluator +use ra_syntax::SmolStr; +use rustc_hash::{FxHashMap, FxHashSet}; + +mod cfg_expr; + +pub use cfg_expr::{parse_cfg, CfgExpr}; + +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct CfgOptions { + atoms: FxHashSet, + features: FxHashSet, + options: FxHashMap, +} + +impl CfgOptions { + pub fn check(&self, cfg: &CfgExpr) -> Option { + cfg.fold(&|key, value| match value { + None => self.atoms.contains(key), + Some(value) if key == "feature" => self.features.contains(value), + Some(value) => self.options.get(key).map_or(false, |v| v == value), + }) + } + + pub fn is_cfg_enabled(&self, attr: &tt::Subtree) -> Option { + self.check(&parse_cfg(attr)) + } + + pub fn atom(mut self, name: SmolStr) -> CfgOptions { + self.atoms.insert(name); + self + } + + pub fn feature(mut self, name: SmolStr) -> CfgOptions { + self.features.insert(name); + self + } + + pub fn option(mut self, key: SmolStr, value: SmolStr) -> CfgOptions { + self.options.insert(key, value); + self + } +} diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml index 2fac07bc5..c141f1a88 100644 --- a/crates/ra_db/Cargo.toml +++ b/crates/ra_db/Cargo.toml @@ -10,4 +10,5 @@ relative-path = "0.4.0" rustc-hash = "1.0" ra_syntax = { path = "../ra_syntax" } +ra_cfg = { path = "../ra_cfg" } ra_prof = { path = "../ra_prof" } diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index 52f892891..5fd6edd78 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs @@ -9,6 +9,7 @@ use relative_path::{RelativePath, RelativePathBuf}; use rustc_hash::FxHashMap; +use ra_cfg::CfgOptions; use ra_syntax::SmolStr; use rustc_hash::FxHashSet; @@ -109,11 +110,13 @@ struct CrateData { file_id: FileId, edition: Edition, dependencies: Vec, + cfg_options: CfgOptions, } impl CrateData { fn new(file_id: FileId, edition: Edition) -> CrateData { - CrateData { file_id, edition, dependencies: Vec::new() } + // FIXME: cfg options + CrateData { file_id, edition, dependencies: Vec::new(), cfg_options: CfgOptions::default() } } fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { @@ -141,6 +144,10 @@ impl CrateGraph { crate_id } + pub fn cfg_options(&self, crate_id: CrateId) -> &CfgOptions { + &self.arena[&crate_id].cfg_options + } + pub fn add_dep( &mut self, from: CrateId, diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml index d9bed4dda..cc117f84d 100644 --- a/crates/ra_hir/Cargo.toml +++ b/crates/ra_hir/Cargo.toml @@ -15,6 +15,7 @@ once_cell = "1.0.1" ra_syntax = { path = "../ra_syntax" } ra_arena = { path = "../ra_arena" } +ra_cfg = { path = "../ra_cfg" } ra_db = { path = "../ra_db" } mbe = { path = "../ra_mbe", package = "ra_mbe" } tt = { path = "../ra_tt", package = "ra_tt" } diff --git a/crates/ra_hir/src/attr.rs b/crates/ra_hir/src/attr.rs new file mode 100644 index 000000000..19be6de32 --- /dev/null +++ b/crates/ra_hir/src/attr.rs @@ -0,0 +1,58 @@ +use mbe::ast_to_token_tree; +use ra_syntax::{ + ast::{self, AstNode}, + SmolStr, +}; +use tt::Subtree; + +use crate::{db::AstDatabase, path::Path, Source}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct Attr { + pub(crate) path: Path, + pub(crate) input: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AttrInput { + Literal(SmolStr), + TokenTree(Subtree), +} + +impl Attr { + pub(crate) fn from_src( + Source { file_id, ast }: Source, + db: &impl AstDatabase, + ) -> Option { + let path = Path::from_src(Source { file_id, ast: ast.path()? }, db)?; + let input = match ast.input() { + None => None, + Some(ast::AttrInput::Literal(lit)) => { + // FIXME: escape? raw string? + let value = lit.syntax().first_token()?.text().trim_matches('"').into(); + Some(AttrInput::Literal(value)) + } + Some(ast::AttrInput::TokenTree(tt)) => { + Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0)) + } + }; + + Some(Attr { path, input }) + } + + pub(crate) fn is_simple_atom(&self, name: &str) -> bool { + // FIXME: Avoid cloning + self.path.as_ident().map_or(false, |s| s.to_string() == name) + } + + pub(crate) fn as_cfg(&self) -> Option<&Subtree> { + if self.is_simple_atom("cfg") { + match &self.input { + Some(AttrInput::TokenTree(subtree)) => Some(subtree), + _ => None, + } + } else { + None + } + } +} diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 00031deba..4340e9d34 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -44,6 +44,7 @@ mod traits; mod type_alias; mod type_ref; mod ty; +mod attr; mod impl_block; mod expr; mod lang_item; diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 40e56dfe0..f0e790e4c 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -1,11 +1,13 @@ //! FIXME: write short doc here +use ra_cfg::CfgOptions; use ra_db::FileId; use ra_syntax::{ast, SmolStr}; use rustc_hash::FxHashMap; use test_utils::tested_by; use crate::{ + attr::Attr, db::DefDatabase, ids::{AstItemDef, LocationCtx, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind}, name::MACRO_RULES, @@ -35,6 +37,9 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C } } + let crate_graph = db.crate_graph(); + let cfg_options = crate_graph.cfg_options(def_map.krate().crate_id()); + let mut collector = DefCollector { db, def_map, @@ -42,6 +47,7 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C unresolved_imports: Vec::new(), unexpanded_macros: Vec::new(), macro_stack_monitor: MacroStackMonitor::default(), + cfg_options, }; collector.collect(); collector.finish() @@ -76,8 +82,8 @@ impl MacroStackMonitor { } /// Walks the tree of module recursively -struct DefCollector { - db: DB, +struct DefCollector<'a, DB> { + db: &'a DB, def_map: CrateDefMap, glob_imports: FxHashMap>, unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, @@ -86,9 +92,11 @@ struct DefCollector { /// Some macro use `$tt:tt which mean we have to handle the macro perfectly /// To prevent stack overflow, we add a deep counter here for prevent that. macro_stack_monitor: MacroStackMonitor, + + cfg_options: &'a CfgOptions, } -impl<'a, DB> DefCollector<&'a DB> +impl DefCollector<'_, DB> where DB: DefDatabase, { @@ -506,7 +514,7 @@ struct ModCollector<'a, D> { parent_module: Option>, } -impl ModCollector<'_, &'_ mut DefCollector<&'_ DB>> +impl ModCollector<'_, &'_ mut DefCollector<'_, DB>> where DB: DefDatabase, { @@ -523,23 +531,27 @@ where // `#[macro_use] extern crate` is hoisted to imports macros before collecting // any other items. for item in items { - if let raw::RawItemKind::Import(import_id) = item.kind { - let import = self.raw_items[import_id].clone(); - if import.is_extern_crate && import.is_macro_use { - self.def_collector.import_macros_from_extern_crate(self.module_id, &import); + if self.is_cfg_enabled(&item.attrs) { + if let raw::RawItemKind::Import(import_id) = item.kind { + let import = self.raw_items[import_id].clone(); + if import.is_extern_crate && import.is_macro_use { + self.def_collector.import_macros_from_extern_crate(self.module_id, &import); + } } } } for item in items { - match item.kind { - raw::RawItemKind::Module(m) => self.collect_module(&self.raw_items[m]), - raw::RawItemKind::Import(import_id) => self - .def_collector - .unresolved_imports - .push((self.module_id, import_id, self.raw_items[import_id].clone())), - raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]), - raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]), + if self.is_cfg_enabled(&item.attrs) { + match item.kind { + raw::RawItemKind::Module(m) => self.collect_module(&self.raw_items[m]), + raw::RawItemKind::Import(import_id) => self + .def_collector + .unresolved_imports + .push((self.module_id, import_id, self.raw_items[import_id].clone())), + raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]), + raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]), + } } } } @@ -702,6 +714,13 @@ where self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_); } } + + fn is_cfg_enabled(&self, attrs: &[Attr]) -> bool { + attrs + .iter() + .flat_map(|attr| attr.as_cfg()) + .all(|cfg| self.def_collector.cfg_options.is_cfg_enabled(cfg).unwrap_or(true)) + } } fn is_macro_rules(path: &Path) -> bool { @@ -729,6 +748,7 @@ mod tests { unresolved_imports: Vec::new(), unexpanded_macros: Vec::new(), macro_stack_monitor: monitor, + cfg_options: &CfgOptions::default(), }; collector.collect(); collector.finish() diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index cacbcb517..ff079bcf1 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -2,7 +2,6 @@ use std::{ops::Index, sync::Arc}; -use mbe::ast_to_token_tree; use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId}; use ra_syntax::{ ast::{self, AttrsOwner, NameOwner}, @@ -11,6 +10,7 @@ use ra_syntax::{ use test_utils::tested_by; use crate::{ + attr::Attr, db::{AstDatabase, DefDatabase}, AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, Source, }; @@ -29,8 +29,6 @@ pub struct RawItems { items: Vec, } -type Attrs = Arc<[tt::Subtree]>; - #[derive(Debug, Default, PartialEq, Eq)] pub struct ImportSourceMap { map: ArenaMap, @@ -124,7 +122,7 @@ impl Index for RawItems { #[derive(Debug, PartialEq, Eq, Clone)] pub(super) struct RawItem { - pub(super) attrs: Attrs, + pub(super) attrs: Arc<[Attr]>, pub(super) kind: RawItemKind, } @@ -285,6 +283,7 @@ impl RawItemsCollector<&DB> { let attrs = self.parse_attrs(&module); let ast_id = self.source_ast_id_map.ast_id(&module); + // FIXME: cfg_attr let is_macro_use = module.has_atom_attr("macro_use"); if module.has_semi() { let attr_path = extract_mod_path_attribute(&module); @@ -315,6 +314,7 @@ impl RawItemsCollector<&DB> { } fn add_use_item(&mut self, current_module: Option, use_item: ast::UseItem) { + // FIXME: cfg_attr let is_prelude = use_item.has_atom_attr("prelude_import"); let attrs = self.parse_attrs(&use_item); @@ -349,6 +349,7 @@ impl RawItemsCollector<&DB> { let path = Path::from_name_ref(&name_ref); let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); let attrs = self.parse_attrs(&extern_crate); + // FIXME: cfg_attr let is_macro_use = extern_crate.has_atom_attr("macro_use"); let import_data = ImportData { path, @@ -368,6 +369,7 @@ impl RawItemsCollector<&DB> { } fn add_macro(&mut self, current_module: Option, m: ast::MacroCall) { + let attrs = self.parse_attrs(&m); let path = match m .path() .and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db)) @@ -378,6 +380,7 @@ impl RawItemsCollector<&DB> { let name = m.name().map(|it| it.as_name()); let ast_id = self.source_ast_id_map.ast_id(&m); + // FIXME: cfg_attr let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export"); let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); @@ -387,7 +390,7 @@ impl RawItemsCollector<&DB> { fn push_import( &mut self, current_module: Option, - attrs: Attrs, + attrs: Arc<[Attr]>, data: ImportData, source: ImportSourcePtr, ) { @@ -396,7 +399,7 @@ impl RawItemsCollector<&DB> { self.push_item(current_module, attrs, RawItemKind::Import(import)) } - fn push_item(&mut self, current_module: Option, attrs: Attrs, kind: RawItemKind) { + fn push_item(&mut self, current_module: Option, attrs: Arc<[Attr]>, kind: RawItemKind) { match current_module { Some(module) => match &mut self.raw_items.modules[module] { ModuleData::Definition { items, .. } => items, @@ -407,11 +410,9 @@ impl RawItemsCollector<&DB> { .push(RawItem { attrs, kind }) } - fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs { + fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Arc<[Attr]> { item.attrs() - .flat_map(|attr| attr.value()) - .flat_map(|tt| ast_to_token_tree(&tt)) - .map(|(tt, _)| tt) + .flat_map(|ast| Attr::from_src(Source { ast, file_id: self.file_id }, self.db)) .collect() } } -- cgit v1.2.3 From d2ea776b8fbb5286a04dde75a9a8f8b14f12bfe9 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Mon, 30 Sep 2019 07:38:16 +0800 Subject: Enable CfgOptions `test` for workspace crates --- crates/ra_db/src/input.rs | 28 ++++++++------ crates/ra_hir/src/mock.rs | 23 +++++++---- crates/ra_hir/src/nameres/tests.rs | 70 ++++++++++++++++++++++++++++++++++ crates/ra_ide_api/Cargo.toml | 1 + crates/ra_ide_api/src/lib.rs | 6 ++- crates/ra_ide_api/src/mock_analysis.rs | 7 +++- crates/ra_ide_api/src/parent_module.rs | 3 +- crates/ra_project_model/Cargo.toml | 1 + crates/ra_project_model/src/lib.rs | 21 ++++++++-- 9 files changed, 134 insertions(+), 26 deletions(-) diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index 5fd6edd78..23148096c 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs @@ -114,9 +114,8 @@ struct CrateData { } impl CrateData { - fn new(file_id: FileId, edition: Edition) -> CrateData { - // FIXME: cfg options - CrateData { file_id, edition, dependencies: Vec::new(), cfg_options: CfgOptions::default() } + fn new(file_id: FileId, edition: Edition, cfg_options: CfgOptions) -> CrateData { + CrateData { file_id, edition, dependencies: Vec::new(), cfg_options } } fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { @@ -137,9 +136,14 @@ impl Dependency { } impl CrateGraph { - pub fn add_crate_root(&mut self, file_id: FileId, edition: Edition) -> CrateId { + pub fn add_crate_root( + &mut self, + file_id: FileId, + edition: Edition, + cfg_options: CfgOptions, + ) -> CrateId { let crate_id = CrateId(self.arena.len() as u32); - let prev = self.arena.insert(crate_id, CrateData::new(file_id, edition)); + let prev = self.arena.insert(crate_id, CrateData::new(file_id, edition, cfg_options)); assert!(prev.is_none()); crate_id } @@ -228,14 +232,14 @@ impl CrateGraph { #[cfg(test)] mod tests { - use super::{CrateGraph, Edition::Edition2018, FileId, SmolStr}; + use super::{CfgOptions, CrateGraph, Edition::Edition2018, FileId, SmolStr}; #[test] fn it_should_panic_because_of_cycle_dependencies() { let mut graph = CrateGraph::default(); - let crate1 = graph.add_crate_root(FileId(1u32), Edition2018); - let crate2 = graph.add_crate_root(FileId(2u32), Edition2018); - let crate3 = graph.add_crate_root(FileId(3u32), Edition2018); + let crate1 = graph.add_crate_root(FileId(1u32), Edition2018, CfgOptions::default()); + let crate2 = graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default()); + let crate3 = graph.add_crate_root(FileId(3u32), Edition2018, CfgOptions::default()); assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok()); assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok()); assert!(graph.add_dep(crate3, SmolStr::new("crate1"), crate1).is_err()); @@ -244,9 +248,9 @@ mod tests { #[test] fn it_works() { let mut graph = CrateGraph::default(); - let crate1 = graph.add_crate_root(FileId(1u32), Edition2018); - let crate2 = graph.add_crate_root(FileId(2u32), Edition2018); - let crate3 = graph.add_crate_root(FileId(3u32), Edition2018); + let crate1 = graph.add_crate_root(FileId(1u32), Edition2018, CfgOptions::default()); + let crate2 = graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default()); + let crate3 = graph.add_crate_root(FileId(3u32), Edition2018, CfgOptions::default()); assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok()); assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok()); } diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 50feb98fb..f750986b8 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -3,6 +3,7 @@ use std::{panic, sync::Arc}; use parking_lot::Mutex; +use ra_cfg::CfgOptions; use ra_db::{ salsa, CrateGraph, CrateId, Edition, FileId, FilePosition, SourceDatabase, SourceRoot, SourceRootId, @@ -74,13 +75,13 @@ impl MockDatabase { pub fn set_crate_graph_from_fixture(&mut self, graph: CrateGraphFixture) { let mut ids = FxHashMap::default(); let mut crate_graph = CrateGraph::default(); - for (crate_name, (crate_root, edition, _)) in graph.0.iter() { + for (crate_name, (crate_root, edition, cfg_options, _)) in graph.0.iter() { let crate_root = self.file_id_of(&crate_root); - let crate_id = crate_graph.add_crate_root(crate_root, *edition); + let crate_id = crate_graph.add_crate_root(crate_root, *edition, cfg_options.clone()); Arc::make_mut(&mut self.crate_names).insert(crate_id, crate_name.clone()); ids.insert(crate_name, crate_id); } - for (crate_name, (_, _, deps)) in graph.0.iter() { + for (crate_name, (_, _, _, deps)) in graph.0.iter() { let from = ids[crate_name]; for dep in deps { let to = ids[dep]; @@ -184,7 +185,7 @@ impl MockDatabase { if is_crate_root { let mut crate_graph = CrateGraph::default(); - crate_graph.add_crate_root(file_id, Edition::Edition2018); + crate_graph.add_crate_root(file_id, Edition::Edition2018, CfgOptions::default()); self.set_crate_graph(Arc::new(crate_graph)); } file_id @@ -268,19 +269,27 @@ impl MockDatabase { } #[derive(Default)] -pub struct CrateGraphFixture(pub Vec<(String, (String, Edition, Vec))>); +pub struct CrateGraphFixture(pub Vec<(String, (String, Edition, CfgOptions, Vec))>); #[macro_export] macro_rules! crate_graph { - ($($crate_name:literal: ($crate_path:literal, $($edition:literal,)? [$($dep:literal),*]),)*) => {{ + ($( + $crate_name:literal: ( + $crate_path:literal, + $($edition:literal,)? + [$($dep:literal),*] + $(,$cfg:expr)? + ), + )*) => {{ let mut res = $crate::mock::CrateGraphFixture::default(); $( #[allow(unused_mut, unused_assignments)] let mut edition = ra_db::Edition::Edition2018; $(edition = ra_db::Edition::from_string($edition);)? + let cfg_options = { ::ra_cfg::CfgOptions::default() $(; $cfg)? }; res.0.push(( $crate_name.to_string(), - ($crate_path.to_string(), edition, vec![$($dep.to_string()),*]) + ($crate_path.to_string(), edition, cfg_options, vec![$($dep.to_string()),*]) )); )* res diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index bc4b47b70..f43767e59 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs @@ -7,6 +7,7 @@ mod mod_resolution; use std::sync::Arc; use insta::assert_snapshot; +use ra_cfg::CfgOptions; use ra_db::SourceDatabase; use test_utils::covers; @@ -507,3 +508,72 @@ fn values_dont_shadow_extern_crates() { ⋮foo: v "###); } + +#[test] +fn cfg_not_test() { + let map = def_map_with_crate_graph( + r#" + //- /main.rs + use {Foo, Bar, Baz}; + //- /lib.rs + #[prelude_import] + pub use self::prelude::*; + mod prelude { + #[cfg(test)] + pub struct Foo; + #[cfg(not(test))] + pub struct Bar; + #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] + pub struct Baz; + } + "#, + crate_graph! { + "main": ("/main.rs", ["std"]), + "std": ("/lib.rs", []), + }, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Baz: _ + ⋮Foo: _ + "###); +} + +#[test] +fn cfg_test() { + let map = def_map_with_crate_graph( + r#" + //- /main.rs + use {Foo, Bar, Baz}; + //- /lib.rs + #[prelude_import] + pub use self::prelude::*; + mod prelude { + #[cfg(test)] + pub struct Foo; + #[cfg(not(test))] + pub struct Bar; + #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] + pub struct Baz; + } + "#, + crate_graph! { + "main": ("/main.rs", ["std"]), + "std": ("/lib.rs", [], CfgOptions::default() + .atom("test".into()) + .feature("foo".into()) + .feature("bar".into()) + .option("opt".into(), "42".into()) + ), + }, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: _ + ⋮Baz: t v + ⋮Foo: t v + "###); +} diff --git a/crates/ra_ide_api/Cargo.toml b/crates/ra_ide_api/Cargo.toml index 6bbf9d5dd..f919a2d61 100644 --- a/crates/ra_ide_api/Cargo.toml +++ b/crates/ra_ide_api/Cargo.toml @@ -23,6 +23,7 @@ rand = { version = "0.7.0", features = ["small_rng"] } ra_syntax = { path = "../ra_syntax" } ra_text_edit = { path = "../ra_text_edit" } ra_db = { path = "../ra_db" } +ra_cfg = { path = "../ra_cfg" } ra_fmt = { path = "../ra_fmt" } ra_prof = { path = "../ra_prof" } hir = { path = "../ra_hir", package = "ra_hir" } diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 44d1ec77b..24f1b91f6 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs @@ -49,6 +49,7 @@ mod test_utils; use std::sync::Arc; +use ra_cfg::CfgOptions; use ra_db::{ salsa::{self, ParallelDatabase}, CheckCanceled, SourceDatabase, @@ -322,7 +323,10 @@ impl Analysis { change.add_root(source_root, true); let mut crate_graph = CrateGraph::default(); let file_id = FileId(0); - crate_graph.add_crate_root(file_id, Edition::Edition2018); + // FIXME: cfg options + // Default to enable test for single file. + let cfg_options = CfgOptions::default().atom("test".into()); + crate_graph.add_crate_root(file_id, Edition::Edition2018, cfg_options); change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); change.set_crate_graph(crate_graph); host.apply_change(change); diff --git a/crates/ra_ide_api/src/mock_analysis.rs b/crates/ra_ide_api/src/mock_analysis.rs index 16870c7ae..13258b63d 100644 --- a/crates/ra_ide_api/src/mock_analysis.rs +++ b/crates/ra_ide_api/src/mock_analysis.rs @@ -2,6 +2,7 @@ use std::sync::Arc; +use ra_cfg::CfgOptions; use relative_path::RelativePathBuf; use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; @@ -93,10 +94,12 @@ impl MockAnalysis { assert!(path.starts_with('/')); let path = RelativePathBuf::from_path(&path[1..]).unwrap(); let file_id = FileId(i as u32 + 1); + // FIXME: cfg options + let cfg_options = CfgOptions::default(); if path == "/lib.rs" || path == "/main.rs" { - root_crate = Some(crate_graph.add_crate_root(file_id, Edition2018)); + root_crate = Some(crate_graph.add_crate_root(file_id, Edition2018, cfg_options)); } else if path.ends_with("/lib.rs") { - let other_crate = crate_graph.add_crate_root(file_id, Edition2018); + let other_crate = crate_graph.add_crate_root(file_id, Edition2018, cfg_options); let crate_name = path.parent().unwrap().file_name().unwrap(); if let Some(root_crate) = root_crate { crate_graph.add_dep(root_crate, crate_name.into(), other_crate).unwrap(); diff --git a/crates/ra_ide_api/src/parent_module.rs b/crates/ra_ide_api/src/parent_module.rs index c85f1d0d0..566509849 100644 --- a/crates/ra_ide_api/src/parent_module.rs +++ b/crates/ra_ide_api/src/parent_module.rs @@ -41,6 +41,7 @@ mod tests { AnalysisChange, CrateGraph, Edition::Edition2018, }; + use ra_cfg::CfgOptions; #[test] fn test_resolve_parent_module() { @@ -88,7 +89,7 @@ mod tests { assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); let mut crate_graph = CrateGraph::default(); - let crate_id = crate_graph.add_crate_root(root_file, Edition2018); + let crate_id = crate_graph.add_crate_root(root_file, Edition2018, CfgOptions::default()); let mut change = AnalysisChange::new(); change.set_crate_graph(crate_graph); host.apply_change(change); diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml index ae6b91aa6..a65100031 100644 --- a/crates/ra_project_model/Cargo.toml +++ b/crates/ra_project_model/Cargo.toml @@ -12,6 +12,7 @@ cargo_metadata = "0.8.2" ra_arena = { path = "../ra_arena" } ra_db = { path = "../ra_db" } +ra_cfg = { path = "../ra_cfg" } serde = { version = "1.0.89", features = ["derive"] } serde_json = "1.0.39" diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 5d3078598..5ff3971e0 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -11,6 +11,7 @@ use std::{ path::{Path, PathBuf}, }; +use ra_cfg::CfgOptions; use ra_db::{CrateGraph, CrateId, Edition, FileId}; use rustc_hash::FxHashMap; use serde_json::from_reader; @@ -131,7 +132,13 @@ impl ProjectWorkspace { json_project::Edition::Edition2015 => Edition::Edition2015, json_project::Edition::Edition2018 => Edition::Edition2018, }; - crates.insert(crate_id, crate_graph.add_crate_root(file_id, edition)); + // FIXME: cfg options + // Default to enable test for workspace crates. + let cfg_options = CfgOptions::default().atom("test".into()); + crates.insert( + crate_id, + crate_graph.add_crate_root(file_id, edition, cfg_options), + ); } } @@ -157,7 +164,11 @@ impl ProjectWorkspace { let mut sysroot_crates = FxHashMap::default(); for krate in sysroot.crates() { if let Some(file_id) = load(krate.root(&sysroot)) { - let crate_id = crate_graph.add_crate_root(file_id, Edition::Edition2018); + // FIXME: cfg options + // Crates from sysroot have `cfg(test)` disabled + let cfg_options = CfgOptions::default(); + let crate_id = + crate_graph.add_crate_root(file_id, Edition::Edition2018, cfg_options); sysroot_crates.insert(krate, crate_id); names.insert(crate_id, krate.name(&sysroot).to_string()); } @@ -186,7 +197,11 @@ impl ProjectWorkspace { let root = tgt.root(&cargo); if let Some(file_id) = load(root) { let edition = pkg.edition(&cargo); - let crate_id = crate_graph.add_crate_root(file_id, edition); + // FIXME: cfg options + // Default to enable test for workspace crates. + let cfg_options = CfgOptions::default().atom("test".into()); + let crate_id = + crate_graph.add_crate_root(file_id, edition, cfg_options); names.insert(crate_id, pkg.name(&cargo).to_string()); if tgt.kind(&cargo) == TargetKind::Lib { lib_tgt = Some(crate_id); -- cgit v1.2.3 From a49ad47e5afa5950f92b77badc6679295101328a Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Mon, 30 Sep 2019 17:47:17 +0800 Subject: Support cfg attribute on impl blocks --- crates/ra_hir/src/attr.rs | 19 ++++++++++++-- crates/ra_hir/src/impl_block.rs | 30 ++++++++++++++++++++--- crates/ra_hir/src/nameres/collector.rs | 5 +--- crates/ra_hir/src/nameres/raw.rs | 4 +-- crates/ra_hir/src/ty/tests.rs | 45 ++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 13 deletions(-) diff --git a/crates/ra_hir/src/attr.rs b/crates/ra_hir/src/attr.rs index 19be6de32..84c36b8da 100644 --- a/crates/ra_hir/src/attr.rs +++ b/crates/ra_hir/src/attr.rs @@ -1,11 +1,14 @@ +use std::sync::Arc; + use mbe::ast_to_token_tree; +use ra_cfg::CfgOptions; use ra_syntax::{ - ast::{self, AstNode}, + ast::{self, AstNode, AttrsOwner}, SmolStr, }; use tt::Subtree; -use crate::{db::AstDatabase, path::Path, Source}; +use crate::{db::AstDatabase, path::Path, HirFileId, Source}; #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct Attr { @@ -40,6 +43,14 @@ impl Attr { Some(Attr { path, input }) } + pub(crate) fn from_attrs_owner( + file_id: HirFileId, + owner: &impl AttrsOwner, + db: &impl AstDatabase, + ) -> Arc<[Attr]> { + owner.attrs().flat_map(|ast| Attr::from_src(Source { file_id, ast }, db)).collect() + } + pub(crate) fn is_simple_atom(&self, name: &str) -> bool { // FIXME: Avoid cloning self.path.as_ident().map_or(false, |s| s.to_string() == name) @@ -55,4 +66,8 @@ impl Attr { None } } + + pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Option { + cfg_options.is_cfg_enabled(self.as_cfg()?) + } } diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 8cf74ddc7..7877c3171 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs @@ -4,12 +4,14 @@ use rustc_hash::FxHashMap; use std::sync::Arc; use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId}; +use ra_cfg::CfgOptions; use ra_syntax::{ ast::{self, AstNode}, AstPtr, }; use crate::{ + attr::Attr, code_model::{Module, ModuleSource}, db::{AstDatabase, DefDatabase, HirDatabase}, generics::HasGenericParams, @@ -176,6 +178,7 @@ pub struct ModuleImplBlocks { impl ModuleImplBlocks { fn collect( db: &(impl DefDatabase + AstDatabase), + cfg_options: &CfgOptions, module: Module, source_map: &mut ImplSourceMap, ) -> Self { @@ -188,11 +191,11 @@ impl ModuleImplBlocks { let src = m.module.definition_source(db); match &src.ast { ModuleSource::SourceFile(node) => { - m.collect_from_item_owner(db, source_map, node, src.file_id) + m.collect_from_item_owner(db, cfg_options, source_map, node, src.file_id) } ModuleSource::Module(node) => { let item_list = node.item_list().expect("inline module should have item list"); - m.collect_from_item_owner(db, source_map, &item_list, src.file_id) + m.collect_from_item_owner(db, cfg_options, source_map, &item_list, src.file_id) } }; m @@ -201,6 +204,7 @@ impl ModuleImplBlocks { fn collect_from_item_owner( &mut self, db: &(impl DefDatabase + AstDatabase), + cfg_options: &CfgOptions, source_map: &mut ImplSourceMap, owner: &dyn ast::ModuleItemOwner, file_id: HirFileId, @@ -208,6 +212,11 @@ impl ModuleImplBlocks { for item in owner.items_with_macros() { match item { ast::ItemOrMacro::Item(ast::ModuleItem::ImplBlock(impl_block_ast)) => { + let attrs = Attr::from_attrs_owner(file_id, &impl_block_ast, db); + if attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false)) { + continue; + } + let impl_block = ImplData::from_ast(db, file_id, self.module, &impl_block_ast); let id = self.impls.alloc(impl_block); for &impl_item in &self.impls[id].items { @@ -218,6 +227,11 @@ impl ModuleImplBlocks { } ast::ItemOrMacro::Item(_) => (), ast::ItemOrMacro::Macro(macro_call) => { + let attrs = Attr::from_attrs_owner(file_id, ¯o_call, db); + if attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false)) { + continue; + } + //FIXME: we should really cut down on the boilerplate required to process a macro let ast_id = db.ast_id_map(file_id).ast_id(¯o_call).with_file_id(file_id); if let Some(path) = macro_call @@ -231,7 +245,13 @@ impl ModuleImplBlocks { if let Some(item_list) = db.parse_or_expand(file_id).and_then(ast::MacroItems::cast) { - self.collect_from_item_owner(db, source_map, &item_list, file_id) + self.collect_from_item_owner( + db, + cfg_options, + source_map, + &item_list, + file_id, + ) } } } @@ -246,8 +266,10 @@ pub(crate) fn impls_in_module_with_source_map_query( module: Module, ) -> (Arc, Arc) { let mut source_map = ImplSourceMap::default(); + let crate_graph = db.crate_graph(); + let cfg_options = crate_graph.cfg_options(module.krate.crate_id()); - let result = ModuleImplBlocks::collect(db, module, &mut source_map); + let result = ModuleImplBlocks::collect(db, cfg_options, module, &mut source_map); (Arc::new(result), Arc::new(source_map)) } diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index f0e790e4c..1d79cbd8c 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -716,10 +716,7 @@ where } fn is_cfg_enabled(&self, attrs: &[Attr]) -> bool { - attrs - .iter() - .flat_map(|attr| attr.as_cfg()) - .all(|cfg| self.def_collector.cfg_options.is_cfg_enabled(cfg).unwrap_or(true)) + attrs.iter().all(|attr| attr.is_cfg_enabled(&self.def_collector.cfg_options) != Some(false)) } } diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index ff079bcf1..f02d4eb7a 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -411,9 +411,7 @@ impl RawItemsCollector<&DB> { } fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Arc<[Attr]> { - item.attrs() - .flat_map(|ast| Attr::from_src(Source { ast, file_id: self.file_id }, self.db)) - .collect() + Attr::from_attrs_owner(self.file_id, item, self.db) } } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 4df39c191..171aead18 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use insta::assert_snapshot; +use ra_cfg::CfgOptions; use ra_db::{salsa::Database, FilePosition, SourceDatabase}; use ra_syntax::{ algo, @@ -23,6 +24,50 @@ use crate::{ mod never_type; mod coercion; +#[test] +fn cfg_impl_block() { + let (mut db, pos) = MockDatabase::with_position( + r#" +//- /main.rs +use foo::S as T; +struct S; + +#[cfg(test)] +impl S { + fn foo1(&self) -> i32 { 0 } +} + +#[cfg(not(test))] +impl S { + fn foo2(&self) -> i32 { 0 } +} + +fn test() { + let t = (S.foo1(), S.foo2(), T.foo3(), T.foo4()); + t<|>; +} + +//- /foo.rs +struct S; + +#[cfg(not(test))] +impl S { + fn foo3(&self) -> i32 { 0 } +} + +#[cfg(test)] +impl S { + fn foo4(&self) -> i32 { 0 } +} +"#, + ); + db.set_crate_graph_from_fixture(crate_graph! { + "main": ("/main.rs", ["foo"], CfgOptions::default().atom("test".into())), + "foo": ("/foo.rs", []), + }); + assert_eq!("(i32, {unknown}, i32, {unknown})", type_at_pos(&db, pos)); +} + #[test] fn infer_await() { let (mut db, pos) = MockDatabase::with_position( -- cgit v1.2.3 From 43f09ad36ccc1c53c78a66274693e53161c9b2fa Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Thu, 3 Oct 2019 01:20:08 +0800 Subject: Refactor CfgOptions inside --- crates/ra_cfg/src/lib.rs | 26 +++++++++++++++++--------- crates/ra_hir/src/nameres/tests.rs | 6 +++--- crates/ra_ide_api/src/mock_analysis.rs | 1 - 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/crates/ra_cfg/src/lib.rs b/crates/ra_cfg/src/lib.rs index fa5822d8a..dd81a73f4 100644 --- a/crates/ra_cfg/src/lib.rs +++ b/crates/ra_cfg/src/lib.rs @@ -1,24 +1,32 @@ //! ra_cfg defines conditional compiling options, `cfg` attibute parser and evaluator use ra_syntax::SmolStr; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashSet; mod cfg_expr; pub use cfg_expr::{parse_cfg, 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 +/// key-value options like `target_arch="x86"`. +/// +/// Note that for key-value options, one key can have multiple values (but not none). +/// `feature` is an example. We have both `feature="foo"` and `feature="bar"` if features +/// `foo` and `bar` are both enabled. And here, we store key-value options as a set of tuple +/// of key and value in `key_values`. +/// +/// See: https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct CfgOptions { atoms: FxHashSet, - features: FxHashSet, - options: FxHashMap, + key_values: FxHashSet<(SmolStr, SmolStr)>, } impl CfgOptions { pub fn check(&self, cfg: &CfgExpr) -> Option { cfg.fold(&|key, value| match value { None => self.atoms.contains(key), - Some(value) if key == "feature" => self.features.contains(value), - Some(value) => self.options.get(key).map_or(false, |v| v == value), + Some(value) => self.key_values.contains(&(key.clone(), value.clone())), }) } @@ -31,13 +39,13 @@ impl CfgOptions { self } - pub fn feature(mut self, name: SmolStr) -> CfgOptions { - self.features.insert(name); + pub fn key_value(mut self, key: SmolStr, value: SmolStr) -> CfgOptions { + self.key_values.insert((key, value)); self } - pub fn option(mut self, key: SmolStr, value: SmolStr) -> CfgOptions { - self.options.insert(key, value); + pub fn remove_atom(mut self, name: &SmolStr) -> CfgOptions { + self.atoms.remove(name); self } } diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index f43767e59..34dd79574 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs @@ -563,9 +563,9 @@ fn cfg_test() { "main": ("/main.rs", ["std"]), "std": ("/lib.rs", [], CfgOptions::default() .atom("test".into()) - .feature("foo".into()) - .feature("bar".into()) - .option("opt".into(), "42".into()) + .key_value("feature".into(), "foo".into()) + .key_value("feature".into(), "bar".into()) + .key_value("opt".into(), "42".into()) ), }, ); diff --git a/crates/ra_ide_api/src/mock_analysis.rs b/crates/ra_ide_api/src/mock_analysis.rs index 13258b63d..80b71894c 100644 --- a/crates/ra_ide_api/src/mock_analysis.rs +++ b/crates/ra_ide_api/src/mock_analysis.rs @@ -94,7 +94,6 @@ impl MockAnalysis { assert!(path.starts_with('/')); let path = RelativePathBuf::from_path(&path[1..]).unwrap(); let file_id = FileId(i as u32 + 1); - // FIXME: cfg options let cfg_options = CfgOptions::default(); if path == "/lib.rs" || path == "/main.rs" { root_crate = Some(crate_graph.add_crate_root(file_id, Edition2018, cfg_options)); -- cgit v1.2.3 From e0100e63ae2e873f119b905ac77c3355ffb351b0 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Thu, 3 Oct 2019 01:38:56 +0800 Subject: Optimize --- crates/ra_cfg/src/cfg_expr.rs | 2 +- crates/ra_hir/src/attr.rs | 11 ++++++++--- crates/ra_hir/src/impl_block.rs | 8 ++++++-- crates/ra_hir/src/nameres/collector.rs | 9 ++++++--- crates/ra_hir/src/nameres/raw.rs | 11 +++++++---- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs index efeadf462..811f83048 100644 --- a/crates/ra_cfg/src/cfg_expr.rs +++ b/crates/ra_cfg/src/cfg_expr.rs @@ -15,7 +15,7 @@ pub enum CfgExpr { impl CfgExpr { /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. - pub fn fold(&self, query: &impl Fn(&SmolStr, Option<&SmolStr>) -> bool) -> Option { + pub fn fold(&self, query: &dyn Fn(&SmolStr, Option<&SmolStr>) -> bool) -> Option { match self { CfgExpr::Invalid => None, CfgExpr::Atom(name) => Some(query(name, None)), diff --git a/crates/ra_hir/src/attr.rs b/crates/ra_hir/src/attr.rs index 84c36b8da..a8a7e9006 100644 --- a/crates/ra_hir/src/attr.rs +++ b/crates/ra_hir/src/attr.rs @@ -45,10 +45,15 @@ impl Attr { pub(crate) fn from_attrs_owner( file_id: HirFileId, - owner: &impl AttrsOwner, + owner: &dyn AttrsOwner, db: &impl AstDatabase, - ) -> Arc<[Attr]> { - owner.attrs().flat_map(|ast| Attr::from_src(Source { file_id, ast }, db)).collect() + ) -> Option> { + let mut attrs = owner.attrs().peekable(); + if attrs.peek().is_none() { + // Avoid heap allocation + return None; + } + Some(attrs.flat_map(|ast| Attr::from_src(Source { file_id, ast }, db)).collect()) } pub(crate) fn is_simple_atom(&self, name: &str) -> bool { diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 7877c3171..55dfc393b 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs @@ -213,7 +213,9 @@ impl ModuleImplBlocks { match item { ast::ItemOrMacro::Item(ast::ModuleItem::ImplBlock(impl_block_ast)) => { let attrs = Attr::from_attrs_owner(file_id, &impl_block_ast, db); - if attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false)) { + if attrs.map_or(false, |attrs| { + attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false)) + }) { continue; } @@ -228,7 +230,9 @@ impl ModuleImplBlocks { ast::ItemOrMacro::Item(_) => (), ast::ItemOrMacro::Macro(macro_call) => { let attrs = Attr::from_attrs_owner(file_id, ¯o_call, db); - if attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false)) { + if attrs.map_or(false, |attrs| { + attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false)) + }) { continue; } diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 1d79cbd8c..cef2dc9d2 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -7,7 +7,6 @@ use rustc_hash::FxHashMap; use test_utils::tested_by; use crate::{ - attr::Attr, db::DefDatabase, ids::{AstItemDef, LocationCtx, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind}, name::MACRO_RULES, @@ -715,8 +714,12 @@ where } } - fn is_cfg_enabled(&self, attrs: &[Attr]) -> bool { - attrs.iter().all(|attr| attr.is_cfg_enabled(&self.def_collector.cfg_options) != Some(false)) + fn is_cfg_enabled(&self, attrs: &raw::Attrs) -> bool { + attrs.as_ref().map_or(true, |attrs| { + attrs + .iter() + .all(|attr| attr.is_cfg_enabled(&self.def_collector.cfg_options) != Some(false)) + }) } } diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index f02d4eb7a..623b343c4 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -120,9 +120,12 @@ impl Index for RawItems { } } +// Avoid heap allocation on items without attributes. +pub(super) type Attrs = Option>; + #[derive(Debug, PartialEq, Eq, Clone)] pub(super) struct RawItem { - pub(super) attrs: Arc<[Attr]>, + pub(super) attrs: Attrs, pub(super) kind: RawItemKind, } @@ -390,7 +393,7 @@ impl RawItemsCollector<&DB> { fn push_import( &mut self, current_module: Option, - attrs: Arc<[Attr]>, + attrs: Attrs, data: ImportData, source: ImportSourcePtr, ) { @@ -399,7 +402,7 @@ impl RawItemsCollector<&DB> { self.push_item(current_module, attrs, RawItemKind::Import(import)) } - fn push_item(&mut self, current_module: Option, attrs: Arc<[Attr]>, kind: RawItemKind) { + fn push_item(&mut self, current_module: Option, attrs: Attrs, kind: RawItemKind) { match current_module { Some(module) => match &mut self.raw_items.modules[module] { ModuleData::Definition { items, .. } => items, @@ -410,7 +413,7 @@ impl RawItemsCollector<&DB> { .push(RawItem { attrs, kind }) } - fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Arc<[Attr]> { + fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs { Attr::from_attrs_owner(self.file_id, item, self.db) } } -- cgit v1.2.3 From 1067a1c5f649cc206e35b427eaa8d6553280cc96 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Thu, 3 Oct 2019 02:02:53 +0800 Subject: Read default cfgs from rustc --- Cargo.lock | 1 + crates/ra_batch/src/lib.rs | 18 +++++++---- crates/ra_cfg/src/lib.rs | 10 ++++++ crates/ra_lsp_server/Cargo.toml | 1 + crates/ra_lsp_server/src/world.rs | 8 +++-- crates/ra_project_model/src/cargo_workspace.rs | 6 ++++ crates/ra_project_model/src/json_project.rs | 2 ++ crates/ra_project_model/src/lib.rs | 44 ++++++++++++++++++++++---- 8 files changed, 76 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b95f176fc..736f1994e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1032,6 +1032,7 @@ dependencies = [ "lsp-server 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types 0.61.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ra_cfg 0.1.0", "ra_db 0.1.0", "ra_ide_api 0.1.0", "ra_prof 0.1.0", diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs index 939f72037..a5fc2a23e 100644 --- a/crates/ra_batch/src/lib.rs +++ b/crates/ra_batch/src/lib.rs @@ -7,7 +7,7 @@ use rustc_hash::FxHashMap; use crossbeam_channel::{unbounded, Receiver}; use ra_db::{CrateGraph, FileId, SourceRootId}; use ra_ide_api::{AnalysisChange, AnalysisHost, FeatureFlags}; -use ra_project_model::{PackageRoot, ProjectWorkspace}; +use ra_project_model::{get_rustc_cfg_options, PackageRoot, ProjectWorkspace}; use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; use ra_vfs_glob::RustPackageFilterBuilder; @@ -41,11 +41,17 @@ pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap {:?}", path, vfs_file); - vfs_file.map(vfs_file_to_id) - }); + + // FIXME: cfg options? + let default_cfg_options = + get_rustc_cfg_options().atom("test".into()).atom("debug_assertion".into()); + + let (crate_graph, _crate_names) = + ws.to_crate_graph(&default_cfg_options, &mut |path: &Path| { + let vfs_file = vfs.load(path); + log::debug!("vfs file {:?} -> {:?}", path, vfs_file); + vfs_file.map(vfs_file_to_id) + }); log::debug!("crate graph: {:?}", crate_graph); let source_roots = roots diff --git a/crates/ra_cfg/src/lib.rs b/crates/ra_cfg/src/lib.rs index dd81a73f4..e1c92fbba 100644 --- a/crates/ra_cfg/src/lib.rs +++ b/crates/ra_cfg/src/lib.rs @@ -1,4 +1,6 @@ //! ra_cfg defines conditional compiling options, `cfg` attibute parser and evaluator +use std::iter::IntoIterator; + use ra_syntax::SmolStr; use rustc_hash::FxHashSet; @@ -44,6 +46,14 @@ impl CfgOptions { self } + /// Shortcut to set features + pub fn features(mut self, iter: impl IntoIterator) -> CfgOptions { + for feat in iter { + self = self.key_value("feature".into(), feat); + } + self + } + pub fn remove_atom(mut self, name: &SmolStr) -> CfgOptions { self.atoms.remove(name); self diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index 677d81835..aedc55a95 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml @@ -19,6 +19,7 @@ jod-thread = "0.1.0" ra_vfs = "0.4.0" ra_syntax = { path = "../ra_syntax" } ra_db = { path = "../ra_db" } +ra_cfg = { path = "../ra_cfg" } ra_text_edit = { path = "../ra_text_edit" } ra_ide_api = { path = "../ra_ide_api" } lsp-server = "0.2.0" diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index b55046ec9..27da751ab 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs @@ -13,7 +13,7 @@ use ra_ide_api::{ Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, SourceRootId, }; -use ra_project_model::ProjectWorkspace; +use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace}; use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; use relative_path::RelativePathBuf; @@ -97,6 +97,10 @@ impl WorldState { change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string()); } + // FIXME: Read default cfgs from config + let default_cfg_options = + get_rustc_cfg_options().atom("test".into()).atom("debug_assertion".into()); + // Create crate graph from all the workspaces let mut crate_graph = CrateGraph::default(); let mut load = |path: &std::path::Path| { @@ -104,7 +108,7 @@ impl WorldState { vfs_file.map(|f| FileId(f.0)) }; for ws in workspaces.iter() { - let (graph, crate_names) = ws.to_crate_graph(&mut load); + let (graph, crate_names) = ws.to_crate_graph(&default_cfg_options, &mut load); let shift = crate_graph.extend(graph); for (crate_id, name) in crate_names { change.set_debug_crate_name(crate_id.shift(shift), name) diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 805eaa178..28dadea9d 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs @@ -39,6 +39,7 @@ struct PackageData { is_member: bool, dependencies: Vec, edition: Edition, + features: Vec, } #[derive(Debug, Clone)] @@ -91,6 +92,9 @@ impl Package { pub fn edition(self, ws: &CargoWorkspace) -> Edition { ws.packages[self].edition } + pub fn features(self, ws: &CargoWorkspace) -> &[String] { + &ws.packages[self].features + } pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator + 'a { ws.packages[self].targets.iter().cloned() } @@ -144,6 +148,7 @@ impl CargoWorkspace { is_member, edition: Edition::from_string(&meta_pkg.edition), dependencies: Vec::new(), + features: Vec::new(), }); let pkg_data = &mut packages[pkg]; pkg_by_id.insert(meta_pkg.id.clone(), pkg); @@ -164,6 +169,7 @@ impl CargoWorkspace { let dep = PackageDependency { name: dep_node.name, pkg: pkg_by_id[&dep_node.pkg] }; packages[source].dependencies.push(dep); } + packages[source].features.extend(node.features); } Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root }) diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs index 54ddca2cb..b0d339f38 100644 --- a/crates/ra_project_model/src/json_project.rs +++ b/crates/ra_project_model/src/json_project.rs @@ -19,6 +19,8 @@ pub struct Crate { pub(crate) root_module: PathBuf, pub(crate) edition: Edition, pub(crate) deps: Vec, + #[serde(default)] + pub(crate) features: Vec, } #[derive(Clone, Copy, Debug, Deserialize)] diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 5ff3971e0..05e49f5ce 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -9,6 +9,7 @@ use std::{ fs::File, io::BufReader, path::{Path, PathBuf}, + process::Command, }; use ra_cfg::CfgOptions; @@ -118,6 +119,7 @@ impl ProjectWorkspace { pub fn to_crate_graph( &self, + default_cfg_options: &CfgOptions, load: &mut dyn FnMut(&Path) -> Option, ) -> (CrateGraph, FxHashMap) { let mut crate_graph = CrateGraph::default(); @@ -134,7 +136,9 @@ impl ProjectWorkspace { }; // FIXME: cfg options // Default to enable test for workspace crates. - let cfg_options = CfgOptions::default().atom("test".into()); + let cfg_options = default_cfg_options + .clone() + .features(krate.features.iter().map(Into::into)); crates.insert( crate_id, crate_graph.add_crate_root(file_id, edition, cfg_options), @@ -164,9 +168,8 @@ impl ProjectWorkspace { let mut sysroot_crates = FxHashMap::default(); for krate in sysroot.crates() { if let Some(file_id) = load(krate.root(&sysroot)) { - // FIXME: cfg options // Crates from sysroot have `cfg(test)` disabled - let cfg_options = CfgOptions::default(); + let cfg_options = default_cfg_options.clone().remove_atom(&"test".into()); let crate_id = crate_graph.add_crate_root(file_id, Edition::Edition2018, cfg_options); sysroot_crates.insert(krate, crate_id); @@ -197,9 +200,9 @@ impl ProjectWorkspace { let root = tgt.root(&cargo); if let Some(file_id) = load(root) { let edition = pkg.edition(&cargo); - // FIXME: cfg options - // Default to enable test for workspace crates. - let cfg_options = CfgOptions::default().atom("test".into()); + let cfg_options = default_cfg_options + .clone() + .features(pkg.features(&cargo).iter().map(Into::into)); let crate_id = crate_graph.add_crate_root(file_id, edition, cfg_options); names.insert(crate_id, pkg.name(&cargo).to_string()); @@ -301,3 +304,32 @@ fn find_cargo_toml(path: &Path) -> Result { } Err(format!("can't find Cargo.toml at {}", path.display()))? } + +pub fn get_rustc_cfg_options() -> CfgOptions { + let mut cfg_options = CfgOptions::default(); + + match (|| -> Result<_> { + // `cfg(test)` ans `cfg(debug_assertion)` is handled outside, so we suppress them here. + let output = Command::new("rustc").args(&["--print", "cfg", "-O"]).output()?; + if !output.status.success() { + Err("failed to get rustc cfgs")?; + } + Ok(String::from_utf8(output.stdout)?) + })() { + Ok(rustc_cfgs) => { + for line in rustc_cfgs.lines() { + match line.find('=') { + None => cfg_options = cfg_options.atom(line.into()), + Some(pos) => { + let key = &line[..pos]; + let value = line[pos + 1..].trim_matches('"'); + cfg_options = cfg_options.key_value(key.into(), value.into()); + } + } + } + } + Err(e) => log::error!("failed to get rustc cfgs: {}", e), + } + + cfg_options +} -- cgit v1.2.3 From b271cb18d5ab19624e751867df6705cd1e94edbc Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Thu, 3 Oct 2019 02:50:22 +0800 Subject: Add docs --- crates/ra_cfg/src/cfg_expr.rs | 4 ++++ crates/ra_hir/src/attr.rs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs index 811f83048..39d71851c 100644 --- a/crates/ra_cfg/src/cfg_expr.rs +++ b/crates/ra_cfg/src/cfg_expr.rs @@ -1,3 +1,7 @@ +//! The condition expression used in `#[cfg(..)]` attributes. +//! +//! See: https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation + use std::slice::Iter as SliceIter; use ra_syntax::SmolStr; diff --git a/crates/ra_hir/src/attr.rs b/crates/ra_hir/src/attr.rs index a8a7e9006..f67e80bfd 100644 --- a/crates/ra_hir/src/attr.rs +++ b/crates/ra_hir/src/attr.rs @@ -1,3 +1,5 @@ +//! A higher level attributes based on TokenTree, with also some shortcuts. + use std::sync::Arc; use mbe::ast_to_token_tree; -- cgit v1.2.3 From c6303d9fee98232ac83a77f943c39d65c9c6b6db Mon Sep 17 00:00:00 2001 From: oxalica Date: Sat, 5 Oct 2019 20:55:27 +0800 Subject: Use raw cfgs in json project and fix typo --- crates/ra_lsp_server/tests/heavy_tests/main.rs | 8 +++++++- crates/ra_project_model/src/json_project.rs | 5 +++-- crates/ra_project_model/src/lib.rs | 14 ++++++++------ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index 152681062..2ba82ab05 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs @@ -286,7 +286,13 @@ fn test_missing_module_code_action_in_json_project() { let project = json!({ "roots": [path], - "crates": [ { "root_module": path.join("src/lib.rs"), "deps": [], "edition": "2015" } ] + "crates": [ { + "root_module": path.join("src/lib.rs"), + "deps": [], + "edition": "2015", + "atom_cfgs": [], + "key_value_cfgs": {} + } ] }); let code = format!( diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs index b0d339f38..1bacb1d09 100644 --- a/crates/ra_project_model/src/json_project.rs +++ b/crates/ra_project_model/src/json_project.rs @@ -2,6 +2,7 @@ use std::path::PathBuf; +use rustc_hash::{FxHashMap, FxHashSet}; use serde::Deserialize; /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in @@ -19,8 +20,8 @@ pub struct Crate { pub(crate) root_module: PathBuf, pub(crate) edition: Edition, pub(crate) deps: Vec, - #[serde(default)] - pub(crate) features: Vec, + pub(crate) atom_cfgs: FxHashSet, + pub(crate) key_value_cfgs: FxHashMap, } #[derive(Clone, Copy, Debug, Deserialize)] diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 05e49f5ce..640a5ebd3 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -134,11 +134,13 @@ impl ProjectWorkspace { json_project::Edition::Edition2015 => Edition::Edition2015, json_project::Edition::Edition2018 => Edition::Edition2018, }; - // FIXME: cfg options - // Default to enable test for workspace crates. - let cfg_options = default_cfg_options - .clone() - .features(krate.features.iter().map(Into::into)); + let mut cfg_options = default_cfg_options.clone(); + for name in &krate.atom_cfgs { + cfg_options = cfg_options.atom(name.into()); + } + for (key, value) in &krate.key_value_cfgs { + cfg_options = cfg_options.key_value(key.into(), value.into()); + } crates.insert( crate_id, crate_graph.add_crate_root(file_id, edition, cfg_options), @@ -309,7 +311,7 @@ pub fn get_rustc_cfg_options() -> CfgOptions { let mut cfg_options = CfgOptions::default(); match (|| -> Result<_> { - // `cfg(test)` ans `cfg(debug_assertion)` is handled outside, so we suppress them here. + // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. let output = Command::new("rustc").args(&["--print", "cfg", "-O"]).output()?; if !output.status.success() { Err("failed to get rustc cfgs")?; -- cgit v1.2.3