mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-23 02:27:39 +03:00
Auto merge of #150310 - JonathanBrouwer:cfg_trace2, r=jdonszelmann
Port `#[cfg]` and `#[cfg_attr]` trace attributes to the new attribute parsers This PR converts `cfg` and `cfg_trace` attributes to the new parsed representation. The primary challenge is that re-parsing these attributes in the HIR is a performance regression, since these attributes were only used in rustdoc and clippy parsing them in the HIR is extra work that was not done in the compiler before. To solve this, we only parse the attributes once and then store their parsed representation in the AST.
This commit is contained in:
+4
-125
@@ -7,7 +7,6 @@
|
||||
use std::{fmt, mem, ops};
|
||||
|
||||
use itertools::Either;
|
||||
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::thin_vec::{ThinVec, thin_vec};
|
||||
use rustc_hir as hir;
|
||||
@@ -29,12 +28,6 @@
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub(crate) struct Cfg(CfgEntry);
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub(crate) struct InvalidCfgError {
|
||||
pub(crate) msg: &'static str,
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
/// Whether the configuration consists of just `Cfg` or `Not`.
|
||||
fn is_simple_cfg(cfg: &CfgEntry) -> bool {
|
||||
match cfg {
|
||||
@@ -105,106 +98,6 @@ fn should_capitalize_first_letter(cfg: &CfgEntry) -> bool {
|
||||
}
|
||||
|
||||
impl Cfg {
|
||||
/// Parses a `MetaItemInner` into a `Cfg`.
|
||||
fn parse_nested(
|
||||
nested_cfg: &MetaItemInner,
|
||||
exclude: &FxHashSet<NameValueCfg>,
|
||||
) -> Result<Option<Cfg>, InvalidCfgError> {
|
||||
match nested_cfg {
|
||||
MetaItemInner::MetaItem(cfg) => Cfg::parse_without(cfg, exclude),
|
||||
MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
|
||||
Ok(Some(Cfg(CfgEntry::Bool(*b, DUMMY_SP))))
|
||||
}
|
||||
MetaItemInner::Lit(lit) => {
|
||||
Err(InvalidCfgError { msg: "unexpected literal", span: lit.span })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_without(
|
||||
cfg: &MetaItem,
|
||||
exclude: &FxHashSet<NameValueCfg>,
|
||||
) -> Result<Option<Cfg>, InvalidCfgError> {
|
||||
let name = match cfg.ident() {
|
||||
Some(ident) => ident.name,
|
||||
None => {
|
||||
return Err(InvalidCfgError {
|
||||
msg: "expected a single identifier",
|
||||
span: cfg.span,
|
||||
});
|
||||
}
|
||||
};
|
||||
match cfg.kind {
|
||||
MetaItemKind::Word => {
|
||||
if exclude.contains(&NameValueCfg::new(name)) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(Cfg(CfgEntry::NameValue { name, value: None, span: DUMMY_SP })))
|
||||
}
|
||||
}
|
||||
MetaItemKind::NameValue(ref lit) => match lit.kind {
|
||||
LitKind::Str(value, _) => {
|
||||
if exclude.contains(&NameValueCfg::new_value(name, value)) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(Cfg(CfgEntry::NameValue {
|
||||
name,
|
||||
value: Some(value),
|
||||
span: DUMMY_SP,
|
||||
})))
|
||||
}
|
||||
}
|
||||
_ => Err(InvalidCfgError {
|
||||
// FIXME: if the main #[cfg] syntax decided to support non-string literals,
|
||||
// this should be changed as well.
|
||||
msg: "value of cfg option should be a string literal",
|
||||
span: lit.span,
|
||||
}),
|
||||
},
|
||||
MetaItemKind::List(ref items) => {
|
||||
let orig_len = items.len();
|
||||
let mut sub_cfgs =
|
||||
items.iter().filter_map(|i| Cfg::parse_nested(i, exclude).transpose());
|
||||
let ret = match name {
|
||||
sym::all => {
|
||||
sub_cfgs.try_fold(Cfg(CfgEntry::Bool(true, DUMMY_SP)), |x, y| Ok(x & y?))
|
||||
}
|
||||
sym::any => {
|
||||
sub_cfgs.try_fold(Cfg(CfgEntry::Bool(false, DUMMY_SP)), |x, y| Ok(x | y?))
|
||||
}
|
||||
sym::not => {
|
||||
if orig_len == 1 {
|
||||
let mut sub_cfgs = sub_cfgs.collect::<Vec<_>>();
|
||||
if sub_cfgs.len() == 1 {
|
||||
Ok(!sub_cfgs.pop().unwrap()?)
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
} else {
|
||||
Err(InvalidCfgError { msg: "expected 1 cfg-pattern", span: cfg.span })
|
||||
}
|
||||
}
|
||||
_ => Err(InvalidCfgError { msg: "invalid predicate", span: cfg.span }),
|
||||
};
|
||||
match ret {
|
||||
Ok(c) => Ok(Some(c)),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a `MetaItem` into a `Cfg`.
|
||||
///
|
||||
/// The `MetaItem` should be the content of the `#[cfg(...)]`, e.g., `unix` or
|
||||
/// `target_os = "redox"`.
|
||||
///
|
||||
/// If the content is not properly formatted, it will return an error indicating what and where
|
||||
/// the error is.
|
||||
pub(crate) fn parse(cfg: &MetaItemInner) -> Result<Cfg, InvalidCfgError> {
|
||||
Self::parse_nested(cfg, &FxHashSet::default()).map(|ret| ret.unwrap())
|
||||
}
|
||||
|
||||
/// Renders the configuration for human display, as a short HTML description.
|
||||
pub(crate) fn render_short_html(&self) -> String {
|
||||
let mut msg = Display(&self.0, Format::ShortHtml).to_string();
|
||||
@@ -644,10 +537,6 @@ impl NameValueCfg {
|
||||
fn new(name: Symbol) -> Self {
|
||||
Self { name, value: None }
|
||||
}
|
||||
|
||||
fn new_value(name: Symbol, value: Symbol) -> Self {
|
||||
Self { name, value: Some(value) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a CfgEntry> for NameValueCfg {
|
||||
@@ -751,15 +640,6 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
|
||||
tcx: TyCtxt<'_>,
|
||||
cfg_info: &mut CfgInfo,
|
||||
) -> Option<Arc<Cfg>> {
|
||||
fn single<T: IntoIterator>(it: T) -> Option<T::Item> {
|
||||
let mut iter = it.into_iter();
|
||||
let item = iter.next()?;
|
||||
if iter.next().is_some() {
|
||||
return None;
|
||||
}
|
||||
Some(item)
|
||||
}
|
||||
|
||||
fn check_changed_auto_active_status(
|
||||
changed_auto_active_status: &mut Option<rustc_span::Span>,
|
||||
attr_span: Span,
|
||||
@@ -859,12 +739,11 @@ fn check_changed_auto_active_status(
|
||||
}
|
||||
continue;
|
||||
} else if !cfg_info.parent_is_doc_cfg
|
||||
&& let Some(name) = attr.name()
|
||||
&& matches!(name, sym::cfg | sym::cfg_trace)
|
||||
&& let Some(attr) = single(attr.meta_item_list()?)
|
||||
&& let Ok(new_cfg) = Cfg::parse(&attr)
|
||||
&& let hir::Attribute::Parsed(AttributeKind::CfgTrace(cfgs)) = attr
|
||||
{
|
||||
cfg_info.current_cfg &= new_cfg;
|
||||
for (new_cfg, _) in cfgs {
|
||||
cfg_info.current_cfg &= Cfg(new_cfg.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
use rustc_ast::ast::LitIntType;
|
||||
use rustc_ast::{MetaItemInner, MetaItemLit, Path, Safety, StrStyle};
|
||||
use rustc_data_structures::thin_vec::thin_vec;
|
||||
use rustc_hir::attrs::CfgEntry;
|
||||
use rustc_span::symbol::{Ident, kw};
|
||||
use rustc_span::{DUMMY_SP, create_default_session_globals_then};
|
||||
|
||||
use super::*;
|
||||
@@ -28,10 +25,6 @@ fn name_value_cfg_e(name: &str, value: &str) -> CfgEntry {
|
||||
}
|
||||
}
|
||||
|
||||
fn dummy_lit(symbol: Symbol, kind: LitKind) -> MetaItemInner {
|
||||
MetaItemInner::Lit(MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP })
|
||||
}
|
||||
|
||||
fn cfg_all(v: ThinVec<CfgEntry>) -> Cfg {
|
||||
Cfg(cfg_all_e(v))
|
||||
}
|
||||
@@ -52,51 +45,6 @@ fn cfg_not(v: CfgEntry) -> Cfg {
|
||||
Cfg(CfgEntry::Not(Box::new(v), DUMMY_SP))
|
||||
}
|
||||
|
||||
fn dummy_meta_item_word(name: &str) -> MetaItemInner {
|
||||
MetaItemInner::MetaItem(MetaItem {
|
||||
unsafety: Safety::Default,
|
||||
path: Path::from_ident(Ident::from_str(name)),
|
||||
kind: MetaItemKind::Word,
|
||||
span: DUMMY_SP,
|
||||
})
|
||||
}
|
||||
|
||||
fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> MetaItemInner {
|
||||
let lit = MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP };
|
||||
MetaItemInner::MetaItem(MetaItem {
|
||||
unsafety: Safety::Default,
|
||||
path: Path::from_ident(Ident::from_str(name)),
|
||||
kind: MetaItemKind::NameValue(lit),
|
||||
span: DUMMY_SP,
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! dummy_meta_item_list {
|
||||
($name:ident, [$($list:ident),* $(,)?]) => {
|
||||
MetaItemInner::MetaItem(MetaItem {
|
||||
unsafety: Safety::Default,
|
||||
path: Path::from_ident(Ident::from_str(stringify!($name))),
|
||||
kind: MetaItemKind::List(thin_vec![
|
||||
$(
|
||||
dummy_meta_item_word(stringify!($list)),
|
||||
)*
|
||||
]),
|
||||
span: DUMMY_SP,
|
||||
})
|
||||
};
|
||||
|
||||
($name:ident, [$($list:expr),* $(,)?]) => {
|
||||
MetaItemInner::MetaItem(MetaItem {
|
||||
unsafety: Safety::Default,
|
||||
path: Path::from_ident(Ident::from_str(stringify!($name))),
|
||||
kind: MetaItemKind::List(thin_vec![
|
||||
$($list,)*
|
||||
]),
|
||||
span: DUMMY_SP,
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
fn cfg_true() -> Cfg {
|
||||
Cfg(CfgEntry::Bool(true, DUMMY_SP))
|
||||
}
|
||||
@@ -303,87 +251,6 @@ fn test_cfg_or() {
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_ok() {
|
||||
create_default_session_globals_then(|| {
|
||||
let r#true = Symbol::intern("true");
|
||||
let mi = dummy_lit(r#true, LitKind::Bool(true));
|
||||
assert_eq!(Cfg::parse(&mi), Ok(cfg_true()));
|
||||
|
||||
let r#false = Symbol::intern("false");
|
||||
let mi = dummy_lit(r#false, LitKind::Bool(false));
|
||||
assert_eq!(Cfg::parse(&mi), Ok(cfg_false()));
|
||||
|
||||
let mi = dummy_meta_item_word("all");
|
||||
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all")));
|
||||
|
||||
let done = Symbol::intern("done");
|
||||
let mi = dummy_meta_item_name_value("all", done, LitKind::Str(done, StrStyle::Cooked));
|
||||
assert_eq!(Cfg::parse(&mi), Ok(name_value_cfg("all", "done")));
|
||||
|
||||
let mi = dummy_meta_item_list!(all, [a, b]);
|
||||
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b")));
|
||||
|
||||
let mi = dummy_meta_item_list!(any, [a, b]);
|
||||
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") | word_cfg("b")));
|
||||
|
||||
let mi = dummy_meta_item_list!(not, [a]);
|
||||
assert_eq!(Cfg::parse(&mi), Ok(!word_cfg("a")));
|
||||
|
||||
let mi = dummy_meta_item_list!(
|
||||
not,
|
||||
[dummy_meta_item_list!(
|
||||
any,
|
||||
[dummy_meta_item_word("a"), dummy_meta_item_list!(all, [b, c]),]
|
||||
),]
|
||||
);
|
||||
assert_eq!(Cfg::parse(&mi), Ok(!(word_cfg("a") | (word_cfg("b") & word_cfg("c")))));
|
||||
|
||||
let mi = dummy_meta_item_list!(all, [a, b, c]);
|
||||
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b") & word_cfg("c")));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_err() {
|
||||
create_default_session_globals_then(|| {
|
||||
let mi = dummy_meta_item_name_value("foo", kw::False, LitKind::Bool(false));
|
||||
assert!(Cfg::parse(&mi).is_err());
|
||||
|
||||
let mi = dummy_meta_item_list!(not, [a, b]);
|
||||
assert!(Cfg::parse(&mi).is_err());
|
||||
|
||||
let mi = dummy_meta_item_list!(not, []);
|
||||
assert!(Cfg::parse(&mi).is_err());
|
||||
|
||||
let mi = dummy_meta_item_list!(foo, []);
|
||||
assert!(Cfg::parse(&mi).is_err());
|
||||
|
||||
let mi = dummy_meta_item_list!(
|
||||
all,
|
||||
[dummy_meta_item_list!(foo, []), dummy_meta_item_word("b"),]
|
||||
);
|
||||
assert!(Cfg::parse(&mi).is_err());
|
||||
|
||||
let mi = dummy_meta_item_list!(
|
||||
any,
|
||||
[dummy_meta_item_word("a"), dummy_meta_item_list!(foo, []),]
|
||||
);
|
||||
assert!(Cfg::parse(&mi).is_err());
|
||||
|
||||
let mi = dummy_meta_item_list!(not, [dummy_meta_item_list!(foo, []),]);
|
||||
assert!(Cfg::parse(&mi).is_err());
|
||||
|
||||
let c = Symbol::intern("e");
|
||||
let mi = dummy_lit(c, LitKind::Char('e'));
|
||||
assert!(Cfg::parse(&mi).is_err());
|
||||
|
||||
let five = Symbol::intern("5");
|
||||
let mi = dummy_lit(five, LitKind::Int(5.into(), LitIntType::Unsuffixed));
|
||||
assert!(Cfg::parse(&mi).is_err());
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_render_short_html() {
|
||||
create_default_session_globals_then(|| {
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::ExpnKind;
|
||||
use rustc_span::hygiene::{AstPass, MacroKind};
|
||||
use rustc_span::symbol::{Ident, Symbol, kw, sym};
|
||||
use rustc_span::symbol::{Ident, Symbol, kw};
|
||||
use rustc_trait_selection::traits::wf::object_region_bounds;
|
||||
use tracing::{debug, instrument};
|
||||
use utils::*;
|
||||
@@ -2682,17 +2682,13 @@ fn add_without_unwanted_attributes<'hir>(
|
||||
import_parent,
|
||||
));
|
||||
}
|
||||
hir::Attribute::Unparsed(normal) if let [name] = &*normal.path.segments => {
|
||||
if is_inline || *name != sym::cfg_trace {
|
||||
// If it's not a `cfg()` attribute, we keep it.
|
||||
attrs.push((Cow::Borrowed(attr), import_parent));
|
||||
}
|
||||
}
|
||||
// FIXME: make sure to exclude `#[cfg_trace]` here when it is ported to the new parsers
|
||||
hir::Attribute::Parsed(..) => {
|
||||
|
||||
// We discard `#[cfg(...)]` attributes unless we're inlining
|
||||
hir::Attribute::Parsed(AttributeKind::CfgTrace(..)) if !is_inline => {}
|
||||
// We keep all other attributes
|
||||
_ => {
|
||||
attrs.push((Cow::Borrowed(attr), import_parent));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
use rustc_hir::Attribute;
|
||||
use rustc_hir::attrs::{AttributeKind, DocAttribute};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use crate::clean::inline::{load_attrs, merge_attrs};
|
||||
use crate::clean::{CfgInfo, Crate, Item, ItemKind};
|
||||
@@ -39,10 +38,7 @@ fn add_only_cfg_attributes(attrs: &mut Vec<Attribute>, new_attrs: &[Attribute])
|
||||
let mut new_attr = DocAttribute::default();
|
||||
new_attr.cfg = d.cfg.clone();
|
||||
attrs.push(Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr))));
|
||||
} else if let Attribute::Unparsed(normal) = attr
|
||||
&& let [name] = &*normal.path.segments
|
||||
&& *name == sym::cfg_trace
|
||||
{
|
||||
} else if let Attribute::Parsed(AttributeKind::CfgTrace(..)) = attr {
|
||||
// If it's a `cfg()` attribute, we keep it.
|
||||
attrs.push(attr.clone());
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::msrvs::{self, Msrv, MsrvStack};
|
||||
use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind};
|
||||
use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind, AttrItemKind};
|
||||
use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
@@ -604,7 +604,7 @@ fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
|
||||
if attr.has_name(sym::ignore)
|
||||
&& match &attr.kind {
|
||||
AttrKind::Normal(normal_attr) => !matches!(normal_attr.item.args, AttrArgs::Eq { .. }),
|
||||
AttrKind::Normal(normal_attr) => !matches!(normal_attr.item.args, AttrItemKind::Unparsed(AttrArgs::Eq { .. })),
|
||||
AttrKind::DocComment(..) => true,
|
||||
}
|
||||
{
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use rustc_ast::token::{Token, TokenKind};
|
||||
use rustc_ast::tokenstream::TokenTree;
|
||||
use rustc_ast::{AttrArgs, AttrKind};
|
||||
use rustc_ast::{AttrArgs, AttrKind, AttrItemKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::EarlyContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
if let AttrKind::Normal(normal_attr) = &attr.kind {
|
||||
if let AttrArgs::Eq { .. } = &normal_attr.item.args {
|
||||
if let AttrItemKind::Unparsed(AttrArgs::Eq { .. }) = &normal_attr.item.args {
|
||||
// `#[should_panic = ".."]` found, good
|
||||
return;
|
||||
}
|
||||
|
||||
if let AttrArgs::Delimited(args) = &normal_attr.item.args
|
||||
if let AttrItemKind::Unparsed(AttrArgs::Delimited(args)) = &normal_attr.item.args
|
||||
&& let mut tt_iter = args.tokens.iter()
|
||||
&& let Some(TokenTree::Token(
|
||||
Token {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_ast::MetaItemInner;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_ast::AttrItemKind;
|
||||
use rustc_ast::EarlyParsedAttribute;
|
||||
use rustc_span::sym;
|
||||
use rustc_ast::attr::data_structures::CfgEntry;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@@ -32,29 +35,34 @@
|
||||
|
||||
impl EarlyLintPass for CfgNotTest {
|
||||
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &rustc_ast::Attribute) {
|
||||
if attr.has_name(rustc_span::sym::cfg_trace) && contains_not_test(attr.meta_item_list().as_deref(), false) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CFG_NOT_TEST,
|
||||
attr.span,
|
||||
"code is excluded from test builds",
|
||||
|diag| {
|
||||
diag.help("consider not excluding any code from test builds");
|
||||
diag.note_once("this could increase code coverage despite not actually being tested");
|
||||
},
|
||||
);
|
||||
if attr.has_name(sym::cfg_trace) {
|
||||
let AttrItemKind::Parsed(EarlyParsedAttribute::CfgTrace(cfg)) = &attr.get_normal_item().args else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
if contains_not_test(&cfg, false) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CFG_NOT_TEST,
|
||||
attr.span,
|
||||
"code is excluded from test builds",
|
||||
|diag| {
|
||||
diag.help("consider not excluding any code from test builds");
|
||||
diag.note_once("this could increase code coverage despite not actually being tested");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_not_test(list: Option<&[MetaItemInner]>, not: bool) -> bool {
|
||||
list.is_some_and(|list| {
|
||||
list.iter().any(|item| {
|
||||
item.ident().is_some_and(|ident| match ident.name {
|
||||
rustc_span::sym::not => contains_not_test(item.meta_item_list(), !not),
|
||||
rustc_span::sym::test => not,
|
||||
_ => contains_not_test(item.meta_item_list(), not),
|
||||
})
|
||||
})
|
||||
})
|
||||
fn contains_not_test(cfg: &CfgEntry, not: bool) -> bool {
|
||||
match cfg {
|
||||
CfgEntry::All(subs, _) | CfgEntry::Any(subs, _) => subs.iter().any(|item| {
|
||||
contains_not_test(item, not)
|
||||
}),
|
||||
CfgEntry::Not(sub, _) => contains_not_test(sub, !not),
|
||||
CfgEntry::NameValue { name: sym::test, .. } => not,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use rustc_ast::{AttrArgs, AttrKind, AttrStyle, Attribute};
|
||||
use rustc_ast::{AttrArgs, AttrKind, AttrStyle, Attribute, AttrItemKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::EarlyContext;
|
||||
|
||||
@@ -11,7 +11,7 @@ pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) {
|
||||
if !attr.span.from_expansion()
|
||||
&& let AttrKind::Normal(ref item) = attr.kind
|
||||
&& attr.doc_str().is_some()
|
||||
&& let AttrArgs::Eq { expr: meta, .. } = &item.item.args
|
||||
&& let AttrItemKind::Unparsed(AttrArgs::Eq { expr: meta, .. }) = &item.item.args
|
||||
&& !attr.span.contains(meta.span)
|
||||
// Since the `include_str` is already expanded at this point, we can only take the
|
||||
// whole attribute snippet and then modify for our suggestion.
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::{CrateNum, DefId};
|
||||
use rustc_span::{ExpnKind, Span};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::find_attr;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@@ -268,11 +270,6 @@ fn check_ty(&mut self, cx: &LateContext<'tcx>, hir_ty: &'tcx hir::Ty<'tcx, Ambig
|
||||
/// attribute.
|
||||
fn is_under_cfg_attribute(cx: &LateContext<'_>, hir_id: HirId) -> bool {
|
||||
cx.tcx.hir_parent_id_iter(hir_id).any(|id| {
|
||||
cx.tcx.hir_attrs(id).iter().any(|attr| {
|
||||
matches!(
|
||||
attr.name(),
|
||||
Some(sym::cfg_trace | sym::cfg_attr_trace)
|
||||
)
|
||||
})
|
||||
find_attr!(cx.tcx.hir_attrs(id), AttributeKind::CfgTrace(..) | AttributeKind::CfgAttrTrace)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use rustc_ast::AttrItemKind;
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
@@ -92,7 +93,7 @@ fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
&& let AttrKind::Normal(ref item) = attr.kind
|
||||
&& let Some(doc) = attr.doc_str()
|
||||
&& doc.as_str().len() as u64 > self.max_file_size
|
||||
&& let AttrArgs::Eq { expr: meta, .. } = &item.item.args
|
||||
&& let AttrItemKind::Unparsed(AttrArgs::Eq { expr: meta, .. }) = &item.item.args
|
||||
&& !attr.span.contains(meta.span)
|
||||
// Since the `include_str` is already expanded at this point, we can only take the
|
||||
// whole attribute snippet and then modify for our suggestion.
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context};
|
||||
use rustc_hir::{Expr, HirId};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_span::sym;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::find_attr;
|
||||
|
||||
use super::CONST_IS_EMPTY;
|
||||
|
||||
@@ -40,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_
|
||||
fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool {
|
||||
cx.tcx
|
||||
.hir_parent_id_iter(id)
|
||||
.any(|id| cx.tcx.hir_attrs(id).iter().any(|attr| attr.has_name(sym::cfg_trace)))
|
||||
.any(|id| find_attr!(cx.tcx.hir_attrs(id), AttributeKind::CfgTrace(..)))
|
||||
}
|
||||
|
||||
/// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
use rustc_middle::ty::AssocKind;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::sym;
|
||||
use rustc_hir::Attribute;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@@ -121,7 +123,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
let attrs_sugg = {
|
||||
let mut sugg = String::new();
|
||||
for attr in cx.tcx.hir_attrs(assoc_item_hir_id) {
|
||||
if !attr.has_name(sym::cfg_trace) {
|
||||
let Attribute::Parsed(AttributeKind::CfgTrace(attrs)) = attr else {
|
||||
// This might be some other attribute that the `impl Default` ought to inherit.
|
||||
// But it could also be one of the many attributes that:
|
||||
// - can't be put on an impl block -- like `#[inline]`
|
||||
@@ -131,10 +133,13 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
// reduce the applicability
|
||||
app = Applicability::MaybeIncorrect;
|
||||
continue;
|
||||
};
|
||||
|
||||
for (_, attr_span) in attrs {
|
||||
sugg.push_str(&snippet_with_applicability(cx.sess(), *attr_span, "_", &mut app));
|
||||
sugg.push('\n');
|
||||
}
|
||||
|
||||
sugg.push_str(&snippet_with_applicability(cx.sess(), attr.span(), "_", &mut app));
|
||||
sugg.push('\n');
|
||||
}
|
||||
sugg
|
||||
};
|
||||
|
||||
@@ -976,11 +976,19 @@ pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
|
||||
l.style == r.style
|
||||
&& match (&l.kind, &r.kind) {
|
||||
(DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
|
||||
(Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_attr_args(&l.item.args, &r.item.args),
|
||||
(Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_attr_item_kind(&l.item.args, &r.item.args),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_attr_item_kind(l: &AttrItemKind, r: &AttrItemKind) -> bool {
|
||||
match (l, r) {
|
||||
(AttrItemKind::Unparsed(l), AttrItemKind::Unparsed(r)) => eq_attr_args(l, r),
|
||||
(AttrItemKind::Parsed(_l), AttrItemKind::Parsed(_r)) => todo!(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_attr_args(l: &AttrArgs, r: &AttrArgs) -> bool {
|
||||
use AttrArgs::*;
|
||||
match (l, r) {
|
||||
|
||||
@@ -121,6 +121,7 @@
|
||||
self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
|
||||
TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
|
||||
};
|
||||
use rustc_hir::attrs::CfgEntry;
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::symbol::{Ident, Symbol, kw};
|
||||
@@ -2401,17 +2402,12 @@ pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
|
||||
/// This only checks directly applied attributes, to see if a node is inside a `#[cfg(test)]` parent
|
||||
/// use [`is_in_cfg_test`]
|
||||
pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
|
||||
tcx.hir_attrs(id).iter().any(|attr| {
|
||||
if attr.has_name(sym::cfg_trace)
|
||||
&& let Some(items) = attr.meta_item_list()
|
||||
&& let [item] = &*items
|
||||
&& item.has_name(sym::test)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
if let Some(cfgs) = find_attr!(tcx.hir_attrs(id), AttributeKind::CfgTrace(cfgs) => cfgs)
|
||||
&& cfgs.iter().any(|(cfg, _)| { matches!(cfg, CfgEntry::NameValue { name: sym::test, ..})}) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if any parent node of `HirId` has `#[cfg(test)]` attribute applied
|
||||
@@ -2426,11 +2422,10 @@ pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
|
||||
|
||||
/// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied.
|
||||
pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
tcx.has_attr(def_id, sym::cfg_trace)
|
||||
|| tcx
|
||||
find_attr!(tcx.get_all_attrs(def_id), AttributeKind::CfgTrace(..))
|
||||
|| find_attr!(tcx
|
||||
.hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
|
||||
.flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
|
||||
.any(|attr| attr.has_name(sym::cfg_trace))
|
||||
.flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)), AttributeKind::CfgTrace(..))
|
||||
}
|
||||
|
||||
/// Walks up the HIR tree from the given expression in an attempt to find where the value is
|
||||
|
||||
Reference in New Issue
Block a user