mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 20:43:21 +00:00
Introduce ra_cfg to parse and evaluate CfgExpr
This commit is contained in:
parent
ffe179a736
commit
b1ed887d81
11 changed files with 315 additions and 27 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
@ -922,6 +922,16 @@ dependencies = [
|
||||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "ra_cli"
|
name = "ra_cli"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -941,6 +951,7 @@ dependencies = [
|
||||||
name = "ra_db"
|
name = "ra_db"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ra_cfg 0.1.0",
|
||||||
"ra_prof 0.1.0",
|
"ra_prof 0.1.0",
|
||||||
"ra_syntax 0.1.0",
|
"ra_syntax 0.1.0",
|
||||||
"relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ra_arena 0.1.0",
|
"ra_arena 0.1.0",
|
||||||
|
"ra_cfg 0.1.0",
|
||||||
"ra_db 0.1.0",
|
"ra_db 0.1.0",
|
||||||
"ra_mbe 0.1.0",
|
"ra_mbe 0.1.0",
|
||||||
"ra_prof 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)",
|
"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)",
|
"proptest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ra_assists 0.1.0",
|
"ra_assists 0.1.0",
|
||||||
|
"ra_cfg 0.1.0",
|
||||||
"ra_db 0.1.0",
|
"ra_db 0.1.0",
|
||||||
"ra_fmt 0.1.0",
|
"ra_fmt 0.1.0",
|
||||||
"ra_hir 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)",
|
"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)",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ra_arena 0.1.0",
|
"ra_arena 0.1.0",
|
||||||
|
"ra_cfg 0.1.0",
|
||||||
"ra_db 0.1.0",
|
"ra_db 0.1.0",
|
||||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
14
crates/ra_cfg/Cargo.toml
Normal file
14
crates/ra_cfg/Cargo.toml
Normal file
|
@ -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" }
|
128
crates/ra_cfg/src/cfg_expr.rs
Normal file
128
crates/ra_cfg/src/cfg_expr.rs
Normal file
|
@ -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<CfgExpr>),
|
||||||
|
Any(Vec<CfgExpr>),
|
||||||
|
Not(Box<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<bool> {
|
||||||
|
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<tt::TokenTree>) -> Option<CfgExpr> {
|
||||||
|
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() },
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
43
crates/ra_cfg/src/lib.rs
Normal file
43
crates/ra_cfg/src/lib.rs
Normal file
|
@ -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<SmolStr>,
|
||||||
|
features: FxHashSet<SmolStr>,
|
||||||
|
options: FxHashMap<SmolStr, SmolStr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CfgOptions {
|
||||||
|
pub fn check(&self, cfg: &CfgExpr) -> Option<bool> {
|
||||||
|
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<bool> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,4 +10,5 @@ relative-path = "0.4.0"
|
||||||
rustc-hash = "1.0"
|
rustc-hash = "1.0"
|
||||||
|
|
||||||
ra_syntax = { path = "../ra_syntax" }
|
ra_syntax = { path = "../ra_syntax" }
|
||||||
|
ra_cfg = { path = "../ra_cfg" }
|
||||||
ra_prof = { path = "../ra_prof" }
|
ra_prof = { path = "../ra_prof" }
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
use relative_path::{RelativePath, RelativePathBuf};
|
use relative_path::{RelativePath, RelativePathBuf};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
|
use ra_cfg::CfgOptions;
|
||||||
use ra_syntax::SmolStr;
|
use ra_syntax::SmolStr;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
|
@ -109,11 +110,13 @@ struct CrateData {
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
dependencies: Vec<Dependency>,
|
dependencies: Vec<Dependency>,
|
||||||
|
cfg_options: CfgOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CrateData {
|
impl CrateData {
|
||||||
fn new(file_id: FileId, edition: Edition) -> 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) {
|
fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) {
|
||||||
|
@ -141,6 +144,10 @@ impl CrateGraph {
|
||||||
crate_id
|
crate_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cfg_options(&self, crate_id: CrateId) -> &CfgOptions {
|
||||||
|
&self.arena[&crate_id].cfg_options
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_dep(
|
pub fn add_dep(
|
||||||
&mut self,
|
&mut self,
|
||||||
from: CrateId,
|
from: CrateId,
|
||||||
|
|
|
@ -15,6 +15,7 @@ once_cell = "1.0.1"
|
||||||
|
|
||||||
ra_syntax = { path = "../ra_syntax" }
|
ra_syntax = { path = "../ra_syntax" }
|
||||||
ra_arena = { path = "../ra_arena" }
|
ra_arena = { path = "../ra_arena" }
|
||||||
|
ra_cfg = { path = "../ra_cfg" }
|
||||||
ra_db = { path = "../ra_db" }
|
ra_db = { path = "../ra_db" }
|
||||||
mbe = { path = "../ra_mbe", package = "ra_mbe" }
|
mbe = { path = "../ra_mbe", package = "ra_mbe" }
|
||||||
tt = { path = "../ra_tt", package = "ra_tt" }
|
tt = { path = "../ra_tt", package = "ra_tt" }
|
||||||
|
|
58
crates/ra_hir/src/attr.rs
Normal file
58
crates/ra_hir/src/attr.rs
Normal file
|
@ -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<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 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,6 +44,7 @@ mod traits;
|
||||||
mod type_alias;
|
mod type_alias;
|
||||||
mod type_ref;
|
mod type_ref;
|
||||||
mod ty;
|
mod ty;
|
||||||
|
mod attr;
|
||||||
mod impl_block;
|
mod impl_block;
|
||||||
mod expr;
|
mod expr;
|
||||||
mod lang_item;
|
mod lang_item;
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
|
use ra_cfg::CfgOptions;
|
||||||
use ra_db::FileId;
|
use ra_db::FileId;
|
||||||
use ra_syntax::{ast, SmolStr};
|
use ra_syntax::{ast, SmolStr};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
attr::Attr,
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
ids::{AstItemDef, LocationCtx, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind},
|
ids::{AstItemDef, LocationCtx, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind},
|
||||||
name::MACRO_RULES,
|
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 {
|
let mut collector = DefCollector {
|
||||||
db,
|
db,
|
||||||
def_map,
|
def_map,
|
||||||
|
@ -42,6 +47,7 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C
|
||||||
unresolved_imports: Vec::new(),
|
unresolved_imports: Vec::new(),
|
||||||
unexpanded_macros: Vec::new(),
|
unexpanded_macros: Vec::new(),
|
||||||
macro_stack_monitor: MacroStackMonitor::default(),
|
macro_stack_monitor: MacroStackMonitor::default(),
|
||||||
|
cfg_options,
|
||||||
};
|
};
|
||||||
collector.collect();
|
collector.collect();
|
||||||
collector.finish()
|
collector.finish()
|
||||||
|
@ -76,8 +82,8 @@ impl MacroStackMonitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Walks the tree of module recursively
|
/// Walks the tree of module recursively
|
||||||
struct DefCollector<DB> {
|
struct DefCollector<'a, DB> {
|
||||||
db: DB,
|
db: &'a DB,
|
||||||
def_map: CrateDefMap,
|
def_map: CrateDefMap,
|
||||||
glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>,
|
glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>,
|
||||||
unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>,
|
unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>,
|
||||||
|
@ -86,9 +92,11 @@ struct DefCollector<DB> {
|
||||||
/// Some macro use `$tt:tt which mean we have to handle the macro perfectly
|
/// 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.
|
/// To prevent stack overflow, we add a deep counter here for prevent that.
|
||||||
macro_stack_monitor: MacroStackMonitor,
|
macro_stack_monitor: MacroStackMonitor,
|
||||||
|
|
||||||
|
cfg_options: &'a CfgOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, DB> DefCollector<&'a DB>
|
impl<DB> DefCollector<'_, DB>
|
||||||
where
|
where
|
||||||
DB: DefDatabase,
|
DB: DefDatabase,
|
||||||
{
|
{
|
||||||
|
@ -506,7 +514,7 @@ struct ModCollector<'a, D> {
|
||||||
parent_module: Option<ParentModule<'a>>,
|
parent_module: Option<ParentModule<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>>
|
impl<DB> ModCollector<'_, &'_ mut DefCollector<'_, DB>>
|
||||||
where
|
where
|
||||||
DB: DefDatabase,
|
DB: DefDatabase,
|
||||||
{
|
{
|
||||||
|
@ -523,23 +531,27 @@ where
|
||||||
// `#[macro_use] extern crate` is hoisted to imports macros before collecting
|
// `#[macro_use] extern crate` is hoisted to imports macros before collecting
|
||||||
// any other items.
|
// any other items.
|
||||||
for item in items {
|
for item in items {
|
||||||
if let raw::RawItemKind::Import(import_id) = item.kind {
|
if self.is_cfg_enabled(&item.attrs) {
|
||||||
let import = self.raw_items[import_id].clone();
|
if let raw::RawItemKind::Import(import_id) = item.kind {
|
||||||
if import.is_extern_crate && import.is_macro_use {
|
let import = self.raw_items[import_id].clone();
|
||||||
self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
|
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 {
|
for item in items {
|
||||||
match item.kind {
|
if self.is_cfg_enabled(&item.attrs) {
|
||||||
raw::RawItemKind::Module(m) => self.collect_module(&self.raw_items[m]),
|
match item.kind {
|
||||||
raw::RawItemKind::Import(import_id) => self
|
raw::RawItemKind::Module(m) => self.collect_module(&self.raw_items[m]),
|
||||||
.def_collector
|
raw::RawItemKind::Import(import_id) => self
|
||||||
.unresolved_imports
|
.def_collector
|
||||||
.push((self.module_id, import_id, self.raw_items[import_id].clone())),
|
.unresolved_imports
|
||||||
raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]),
|
.push((self.module_id, import_id, self.raw_items[import_id].clone())),
|
||||||
raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
|
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_);
|
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 {
|
fn is_macro_rules(path: &Path) -> bool {
|
||||||
|
@ -729,6 +748,7 @@ mod tests {
|
||||||
unresolved_imports: Vec::new(),
|
unresolved_imports: Vec::new(),
|
||||||
unexpanded_macros: Vec::new(),
|
unexpanded_macros: Vec::new(),
|
||||||
macro_stack_monitor: monitor,
|
macro_stack_monitor: monitor,
|
||||||
|
cfg_options: &CfgOptions::default(),
|
||||||
};
|
};
|
||||||
collector.collect();
|
collector.collect();
|
||||||
collector.finish()
|
collector.finish()
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
use std::{ops::Index, sync::Arc};
|
use std::{ops::Index, sync::Arc};
|
||||||
|
|
||||||
use mbe::ast_to_token_tree;
|
|
||||||
use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
|
use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, AttrsOwner, NameOwner},
|
ast::{self, AttrsOwner, NameOwner},
|
||||||
|
@ -11,6 +10,7 @@ use ra_syntax::{
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
attr::Attr,
|
||||||
db::{AstDatabase, DefDatabase},
|
db::{AstDatabase, DefDatabase},
|
||||||
AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, Source,
|
AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, Source,
|
||||||
};
|
};
|
||||||
|
@ -29,8 +29,6 @@ pub struct RawItems {
|
||||||
items: Vec<RawItem>,
|
items: Vec<RawItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Attrs = Arc<[tt::Subtree]>;
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq)]
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
pub struct ImportSourceMap {
|
pub struct ImportSourceMap {
|
||||||
map: ArenaMap<ImportId, ImportSourcePtr>,
|
map: ArenaMap<ImportId, ImportSourcePtr>,
|
||||||
|
@ -124,7 +122,7 @@ impl Index<Macro> for RawItems {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub(super) struct RawItem {
|
pub(super) struct RawItem {
|
||||||
pub(super) attrs: Attrs,
|
pub(super) attrs: Arc<[Attr]>,
|
||||||
pub(super) kind: RawItemKind,
|
pub(super) kind: RawItemKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,6 +283,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
|
||||||
let attrs = self.parse_attrs(&module);
|
let attrs = self.parse_attrs(&module);
|
||||||
|
|
||||||
let ast_id = self.source_ast_id_map.ast_id(&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");
|
let is_macro_use = module.has_atom_attr("macro_use");
|
||||||
if module.has_semi() {
|
if module.has_semi() {
|
||||||
let attr_path = extract_mod_path_attribute(&module);
|
let attr_path = extract_mod_path_attribute(&module);
|
||||||
|
@ -315,6 +314,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) {
|
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 is_prelude = use_item.has_atom_attr("prelude_import");
|
||||||
let attrs = self.parse_attrs(&use_item);
|
let attrs = self.parse_attrs(&use_item);
|
||||||
|
|
||||||
|
@ -349,6 +349,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
|
||||||
let path = Path::from_name_ref(&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 alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name());
|
||||||
let attrs = self.parse_attrs(&extern_crate);
|
let attrs = self.parse_attrs(&extern_crate);
|
||||||
|
// FIXME: cfg_attr
|
||||||
let is_macro_use = extern_crate.has_atom_attr("macro_use");
|
let is_macro_use = extern_crate.has_atom_attr("macro_use");
|
||||||
let import_data = ImportData {
|
let import_data = ImportData {
|
||||||
path,
|
path,
|
||||||
|
@ -368,6 +369,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) {
|
fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) {
|
||||||
|
let attrs = self.parse_attrs(&m);
|
||||||
let path = match m
|
let path = match m
|
||||||
.path()
|
.path()
|
||||||
.and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db))
|
.and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db))
|
||||||
|
@ -378,6 +380,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
|
||||||
|
|
||||||
let name = m.name().map(|it| it.as_name());
|
let name = m.name().map(|it| it.as_name());
|
||||||
let ast_id = self.source_ast_id_map.ast_id(&m);
|
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 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 });
|
let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export });
|
||||||
|
@ -387,7 +390,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
|
||||||
fn push_import(
|
fn push_import(
|
||||||
&mut self,
|
&mut self,
|
||||||
current_module: Option<Module>,
|
current_module: Option<Module>,
|
||||||
attrs: Attrs,
|
attrs: Arc<[Attr]>,
|
||||||
data: ImportData,
|
data: ImportData,
|
||||||
source: ImportSourcePtr,
|
source: ImportSourcePtr,
|
||||||
) {
|
) {
|
||||||
|
@ -396,7 +399,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
|
||||||
self.push_item(current_module, attrs, RawItemKind::Import(import))
|
self.push_item(current_module, attrs, RawItemKind::Import(import))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_item(&mut self, current_module: Option<Module>, attrs: Attrs, kind: RawItemKind) {
|
fn push_item(&mut self, current_module: Option<Module>, attrs: Arc<[Attr]>, kind: RawItemKind) {
|
||||||
match current_module {
|
match current_module {
|
||||||
Some(module) => match &mut self.raw_items.modules[module] {
|
Some(module) => match &mut self.raw_items.modules[module] {
|
||||||
ModuleData::Definition { items, .. } => items,
|
ModuleData::Definition { items, .. } => items,
|
||||||
|
@ -407,11 +410,9 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> {
|
||||||
.push(RawItem { attrs, kind })
|
.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()
|
item.attrs()
|
||||||
.flat_map(|attr| attr.value())
|
.flat_map(|ast| Attr::from_src(Source { ast, file_id: self.file_id }, self.db))
|
||||||
.flat_map(|tt| ast_to_token_tree(&tt))
|
|
||||||
.map(|(tt, _)| tt)
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue