-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
00a0125
commit f45b080
Showing
8 changed files
with
302 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
use std::{ | ||
fmt::{self, Debug}, | ||
Check failure on line 2 in crates/cfg/src/cfg_attr.rs GitHub Actions / Rust (ubuntu-latest)
|
||
slice::Iter as SliceIter, | ||
}; | ||
|
||
use crate::{cfg_expr::next_cfg_expr, CfgAtom, CfgExpr}; | ||
Check failure on line 6 in crates/cfg/src/cfg_attr.rs GitHub Actions / Rust (ubuntu-latest)
|
||
use tt::{Delimiter, SmolStr, Span}; | ||
Check failure on line 7 in crates/cfg/src/cfg_attr.rs GitHub Actions / Rust (ubuntu-latest)
|
||
/// Represents a `#[cfg_attr(.., my_attr)]` attribute. | ||
#[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
pub struct CfgAttr<S> { | ||
/// Expression in `cfg_attr` attribute. | ||
pub cfg_expr: CfgExpr, | ||
/// Inner attribute. | ||
pub attr: tt::Subtree<S>, | ||
} | ||
|
||
impl<S: Clone + Span + Debug> CfgAttr<S> { | ||
/// Parses a sub tree in the form of (cfg_expr, inner_attribute) | ||
pub fn parse(tt: &tt::Subtree<S>) -> Option<CfgAttr<S>> { | ||
let mut iter = tt.token_trees.iter(); | ||
let cfg_expr = next_cfg_expr(&mut iter).unwrap_or(CfgExpr::Invalid); | ||
// FIXME: This is probably not the right way to do this | ||
// Get's the span of the next token tree | ||
let first_span = iter.as_slice().first().map(|tt| tt.first_span())?; | ||
let attr = tt::Subtree { | ||
delimiter: Delimiter::invisible_spanned(first_span), | ||
token_trees: iter.cloned().collect(), | ||
}; | ||
Some(CfgAttr { cfg_expr, attr: attr }) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use expect_test::{expect, Expect}; | ||
use mbe::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY}; | ||
use syntax::{ast, AstNode}; | ||
|
||
use crate::{CfgAttr, DnfExpr}; | ||
|
||
fn check_dnf(input: &str, expected_dnf: Expect, expected_attrs: Expect) { | ||
let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); | ||
let Some(CfgAttr { cfg_expr, attr }) = CfgAttr::parse(&tt) else { | ||
assert!(false, "failed to parse cfg_attr"); | ||
return; | ||
}; | ||
|
||
let actual = format!("#![cfg({})]", DnfExpr::new(cfg_expr)); | ||
expected_dnf.assert_eq(&actual); | ||
let actual_attrs = format!("#![{}]", attr); | ||
expected_attrs.assert_eq(&actual_attrs); | ||
} | ||
|
||
#[test] | ||
fn smoke() { | ||
check_dnf( | ||
r#"#![cfg_attr(feature = "nightly", feature(slice_split_at_unchecked))]"#, | ||
expect![[r#"#![cfg(feature = "nightly")]"#]], | ||
expect![r#"#![feature (slice_split_at_unchecked)]"#], | ||
); | ||
|
||
check_dnf( | ||
r#"#![cfg_attr(not(feature = "std"), no_std)]"#, | ||
expect![[r#"#![cfg(not(feature = "std"))]"#]], | ||
expect![r#"#![no_std]"#], | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
use std::os::windows::process; | ||
|
||
use mbe::syntax_node_to_token_tree; | ||
use rustc_hash::FxHashSet; | ||
use syntax::{ | ||
ast::{self, Attr, FieldList, HasAttrs, RecordFieldList, TupleFieldList, Variant, VariantList}, | ||
AstNode, SyntaxElement, SyntaxNode, T, | ||
}; | ||
use tracing::info; | ||
|
||
use crate::{db::ExpandDatabase, span_map::SpanMap, MacroCallLoc}; | ||
|
||
fn check_cfg_attr( | ||
attr: &Attr, | ||
loc: &MacroCallLoc, | ||
span_map: &SpanMap, | ||
db: &dyn ExpandDatabase, | ||
) -> Option<bool> { | ||
attr.simple_name().as_deref().map(|v| v == "cfg")?; | ||
info!("Checking cfg attr {:?}", attr); | ||
let Some(tt) = attr.token_tree() else { | ||
info!("cfg attr has no expr {:?}", attr); | ||
return Some(true); | ||
}; | ||
info!("Checking cfg {:?}", tt); | ||
let tt = tt.syntax().clone(); | ||
// Convert to a tt::Subtree | ||
let tt = syntax_node_to_token_tree(&tt, span_map, loc.call_site); | ||
let cfg = cfg::CfgExpr::parse(&tt); | ||
let enabled = db.crate_graph()[loc.krate].cfg_options.check(&cfg) != Some(false); | ||
Some(enabled) | ||
} | ||
enum CfgAttrResult { | ||
Enabled(Attr), | ||
Disabled, | ||
} | ||
|
||
fn check_cfg_attr_attr( | ||
attr: &Attr, | ||
loc: &MacroCallLoc, | ||
span_map: &SpanMap, | ||
db: &dyn ExpandDatabase, | ||
) -> Option<CfgAttrResult> { | ||
attr.simple_name().as_deref().map(|v| v == "cfg_attr")?; | ||
info!("Checking cfg_attr attr {:?}", attr); | ||
let Some(tt) = attr.token_tree() else { | ||
info!("cfg_attr attr has no expr {:?}", attr); | ||
return None; | ||
}; | ||
info!("Checking cfg_attr {:?}", tt); | ||
let tt = tt.syntax().clone(); | ||
// Convert to a tt::Subtree | ||
let tt = syntax_node_to_token_tree(&tt, span_map, loc.call_site); | ||
let cfg = cfg::CfgExpr::parse(&tt); | ||
let enabled = db.crate_graph()[loc.krate].cfg_options.check(&cfg) != Some(false); | ||
if enabled { | ||
// FIXME: Add the internal attribute | ||
Some(CfgAttrResult::Enabled(attr.clone())) | ||
} else { | ||
Some(CfgAttrResult::Disabled) | ||
} | ||
} | ||
|
||
fn process_has_attrs_with_possible_comma<I: HasAttrs>( | ||
items: impl Iterator<Item = I>, | ||
loc: &MacroCallLoc, | ||
span_map: &SpanMap, | ||
db: &dyn ExpandDatabase, | ||
res: &mut FxHashSet<SyntaxElement>, | ||
) -> Option<()> { | ||
for item in items { | ||
let field_attrs = item.attrs(); | ||
'attrs: for attr in field_attrs { | ||
let Some(enabled) = check_cfg_attr(&attr, loc, span_map, db) else { | ||
continue; | ||
}; | ||
if enabled { | ||
//FIXME: Should we remove the cfg_attr? | ||
} else { | ||
info!("censoring type {:?}", item.syntax()); | ||
res.insert(item.syntax().clone().into()); | ||
// We need to remove the , as well | ||
if let Some(comma) = item.syntax().next_sibling_or_token() { | ||
if comma.kind() == T![,] { | ||
res.insert(comma.into()); | ||
} | ||
} | ||
break 'attrs; | ||
} | ||
let Some(attr_result) = check_cfg_attr_attr(&attr, loc, span_map, db) else { | ||
continue; | ||
}; | ||
match attr_result { | ||
CfgAttrResult::Enabled(attr) => { | ||
//FIXME: Replace the attribute with the internal attribute | ||
} | ||
CfgAttrResult::Disabled => { | ||
info!("censoring type {:?}", item.syntax()); | ||
res.insert(attr.syntax().clone().into()); | ||
continue; | ||
} | ||
} | ||
} | ||
} | ||
Some(()) | ||
} | ||
fn process_enum( | ||
variants: VariantList, | ||
loc: &MacroCallLoc, | ||
span_map: &SpanMap, | ||
db: &dyn ExpandDatabase, | ||
res: &mut FxHashSet<SyntaxElement>, | ||
) -> Option<()> { | ||
for variant in variants.variants() { | ||
'attrs: for attr in variant.attrs() { | ||
if !check_cfg_attr(&attr, loc, span_map, db)? { | ||
info!("censoring variant {:?}", variant.syntax()); | ||
res.insert(variant.syntax().clone().into()); | ||
if let Some(comma) = variant.syntax().next_sibling_or_token() { | ||
if comma.kind() == T![,] { | ||
res.insert(comma.into()); | ||
} | ||
} | ||
break 'attrs; | ||
} | ||
} | ||
if let Some(fields) = variant.field_list() { | ||
match fields { | ||
ast::FieldList::RecordFieldList(fields) => { | ||
process_has_attrs_with_possible_comma(fields.fields(), loc, span_map, db, res)?; | ||
} | ||
ast::FieldList::TupleFieldList(fields) => { | ||
process_has_attrs_with_possible_comma(fields.fields(), loc, span_map, db, res)?; | ||
} | ||
} | ||
} | ||
} | ||
Some(()) | ||
} | ||
/// Handle | ||
pub(crate) fn process_cfg_attrs( | ||
node: &SyntaxNode, | ||
loc: &MacroCallLoc, | ||
span_map: &SpanMap, | ||
db: &dyn ExpandDatabase, | ||
) -> Option<FxHashSet<SyntaxElement>> { | ||
let mut res = FxHashSet::default(); | ||
let item = ast::Item::cast(node.clone())?; | ||
match item { | ||
ast::Item::Struct(it) => match it.field_list()? { | ||
ast::FieldList::RecordFieldList(fields) => { | ||
process_has_attrs_with_possible_comma( | ||
fields.fields(), | ||
loc, | ||
span_map, | ||
db, | ||
&mut res, | ||
)?; | ||
} | ||
ast::FieldList::TupleFieldList(fields) => { | ||
process_has_attrs_with_possible_comma( | ||
fields.fields(), | ||
loc, | ||
span_map, | ||
db, | ||
&mut res, | ||
)?; | ||
} | ||
}, | ||
ast::Item::Enum(it) => { | ||
process_enum(it.variant_list()?, loc, span_map, db, &mut res)?; | ||
} | ||
// FIXME: Implement for other items | ||
_ => {} | ||
} | ||
|
||
Some(res) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.