mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 05:45:12 +00:00
Merge #1928
1928: Support `#[cfg(..)]` r=matklad a=oxalica This PR implement `#[cfg(..)]` conditional compilation. It read default cfg options from `rustc --print cfg` with also hard-coded `test` and `debug_assertion` enabled. Front-end settings are **not** included in this PR. There is also a known issue that inner control attributes are totally ignored. I think it is **not** a part of `cfg` and create a separated issue for it. #1949 Fixes #1920 Related: #1073 Co-authored-by: uHOOCCOOHu <hooccooh1896@gmail.com> Co-authored-by: oxalica <oxalicc@pm.me>
This commit is contained in:
commit
ae6305b90c
29 changed files with 671 additions and 66 deletions
80
crates/ra_hir/src/attr.rs
Normal file
80
crates/ra_hir/src/attr.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
//! A higher level attributes based on TokenTree, with also some shortcuts.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use mbe::ast_to_token_tree;
|
||||
use ra_cfg::CfgOptions;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, AttrsOwner},
|
||||
SmolStr,
|
||||
};
|
||||
use tt::Subtree;
|
||||
|
||||
use crate::{db::AstDatabase, path::Path, HirFileId, Source};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct Attr {
|
||||
pub(crate) path: Path,
|
||||
pub(crate) input: Option<AttrInput>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AttrInput {
|
||||
Literal(SmolStr),
|
||||
TokenTree(Subtree),
|
||||
}
|
||||
|
||||
impl Attr {
|
||||
pub(crate) fn from_src(
|
||||
Source { file_id, ast }: Source<ast::Attr>,
|
||||
db: &impl AstDatabase,
|
||||
) -> Option<Attr> {
|
||||
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 from_attrs_owner(
|
||||
file_id: HirFileId,
|
||||
owner: &dyn AttrsOwner,
|
||||
db: &impl AstDatabase,
|
||||
) -> Option<Arc<[Attr]>> {
|
||||
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 {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Option<bool> {
|
||||
cfg_options.is_cfg_enabled(self.as_cfg()?)
|
||||
}
|
||||
}
|
|
@ -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,13 @@ 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.map_or(false, |attrs| {
|
||||
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 +229,13 @@ impl ModuleImplBlocks {
|
|||
}
|
||||
ast::ItemOrMacro::Item(_) => (),
|
||||
ast::ItemOrMacro::Macro(macro_call) => {
|
||||
let attrs = Attr::from_attrs_owner(file_id, ¯o_call, db);
|
||||
if attrs.map_or(false, |attrs| {
|
||||
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 +249,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 +270,10 @@ pub(crate) fn impls_in_module_with_source_map_query(
|
|||
module: Module,
|
||||
) -> (Arc<ModuleImplBlocks>, Arc<ImplSourceMap>) {
|
||||
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))
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ mod traits;
|
|||
mod type_alias;
|
||||
mod type_ref;
|
||||
mod ty;
|
||||
mod attr;
|
||||
mod impl_block;
|
||||
mod expr;
|
||||
mod lang_item;
|
||||
|
|
|
@ -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<String>))>);
|
||||
pub struct CrateGraphFixture(pub Vec<(String, (String, Edition, CfgOptions, Vec<String>))>);
|
||||
|
||||
#[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
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use ra_cfg::CfgOptions;
|
||||
use ra_db::FileId;
|
||||
use ra_syntax::{ast, SmolStr};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
@ -35,6 +36,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 +46,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 +81,8 @@ impl MacroStackMonitor {
|
|||
}
|
||||
|
||||
/// Walks the tree of module recursively
|
||||
struct DefCollector<DB> {
|
||||
db: DB,
|
||||
struct DefCollector<'a, DB> {
|
||||
db: &'a DB,
|
||||
def_map: CrateDefMap,
|
||||
glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>,
|
||||
unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>,
|
||||
|
@ -86,9 +91,11 @@ struct DefCollector<DB> {
|
|||
/// 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<DB> DefCollector<'_, DB>
|
||||
where
|
||||
DB: DefDatabase,
|
||||
{
|
||||
|
@ -506,7 +513,7 @@ struct ModCollector<'a, D> {
|
|||
parent_module: Option<ParentModule<'a>>,
|
||||
}
|
||||
|
||||
impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>>
|
||||
impl<DB> ModCollector<'_, &'_ mut DefCollector<'_, DB>>
|
||||
where
|
||||
DB: DefDatabase,
|
||||
{
|
||||
|
@ -523,24 +530,27 @@ 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 {
|
||||
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 {
|
||||
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]),
|
||||
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]),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -703,6 +713,14 @@ where
|
|||
self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_);
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn is_macro_rules(path: &Path) -> bool {
|
||||
|
@ -730,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()
|
||||
|
|
|
@ -10,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,
|
||||
};
|
||||
|
@ -119,8 +120,17 @@ impl Index<Macro> for RawItems {
|
|||
}
|
||||
}
|
||||
|
||||
// Avoid heap allocation on items without attributes.
|
||||
pub(super) type Attrs = Option<Arc<[Attr]>>;
|
||||
|
||||
#[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 +225,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
|
|||
}
|
||||
|
||||
fn add_item(&mut self, current_module: Option<Module>, 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 +274,7 @@ impl<DB: AstDatabase> 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,8 +283,10 @@ impl<DB: AstDatabase> 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);
|
||||
// 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);
|
||||
|
@ -283,7 +296,7 @@ impl<DB: AstDatabase> 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,14 +310,16 @@ impl<DB: AstDatabase> 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);
|
||||
}
|
||||
|
||||
fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) {
|
||||
// FIXME: cfg_attr
|
||||
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 +333,12 @@ impl<DB: AstDatabase> 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 +351,8 @@ impl<DB: AstDatabase> 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);
|
||||
// FIXME: cfg_attr
|
||||
let is_macro_use = extern_crate.has_atom_attr("macro_use");
|
||||
let import_data = ImportData {
|
||||
path,
|
||||
|
@ -340,11 +362,17 @@ impl<DB: AstDatabase> 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)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_macro(&mut self, current_module: Option<Module>, 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))
|
||||
|
@ -355,24 +383,26 @@ impl<DB: AstDatabase> 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 });
|
||||
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<Module>,
|
||||
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<Module>, item: RawItem) {
|
||||
fn push_item(&mut self, current_module: Option<Module>, attrs: Attrs, kind: RawItemKind) {
|
||||
match current_module {
|
||||
Some(module) => match &mut self.raw_items.modules[module] {
|
||||
ModuleData::Definition { items, .. } => items,
|
||||
|
@ -380,7 +410,11 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
|
|||
},
|
||||
None => &mut self.raw_items.items,
|
||||
}
|
||||
.push(item)
|
||||
.push(RawItem { attrs, kind })
|
||||
}
|
||||
|
||||
fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs {
|
||||
Attr::from_attrs_owner(self.file_id, item, self.db)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
.key_value("feature".into(), "foo".into())
|
||||
.key_value("feature".into(), "bar".into())
|
||||
.key_value("opt".into(), "42".into())
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
assert_snapshot!(map, @r###"
|
||||
⋮crate
|
||||
⋮Bar: _
|
||||
⋮Baz: t v
|
||||
⋮Foo: t v
|
||||
"###);
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue