Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #21965 from ChayimFriedman2/refactor-cfg-attr
fix: Parse `cfg_attr` and `cfg` specially
Chayim Refael Friedman 6 weeks ago
parent 55825a4 · parent bb17dbe · commit 09dc7ac
-rw-r--r--Cargo.lock3
-rw-r--r--crates/cfg/Cargo.toml3
-rw-r--r--crates/cfg/src/cfg_expr.rs111
-rw-r--r--crates/cfg/src/tests.rs49
-rw-r--r--crates/hir-def/src/attrs.rs223
-rw-r--r--crates/hir-def/src/attrs/docs.rs79
-rw-r--r--crates/hir-def/src/dyn_map.rs2
-rw-r--r--crates/hir-def/src/item_tree/attrs.rs108
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe.rs2
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe/regression.rs10
-rw-r--r--crates/hir-def/src/macro_expansion_tests/proc_macros.rs4
-rw-r--r--crates/hir-expand/Cargo.toml1
-rw-r--r--crates/hir-expand/src/attrs.rs400
-rw-r--r--crates/hir-expand/src/cfg_process.rs59
-rw-r--r--crates/hir-expand/src/db.rs52
-rw-r--r--crates/hir-expand/src/declarative.rs13
-rw-r--r--crates/hir-expand/src/lib.rs11
-rw-r--r--crates/hir/src/lib.rs11
-rw-r--r--crates/hir/src/semantics.rs45
-rw-r--r--crates/hir/src/semantics/child_by_source.rs3
-rw-r--r--crates/hir/src/semantics/source_to_def.rs3
-rw-r--r--crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs2
-rw-r--r--crates/ide-assists/src/handlers/generate_derive.rs8
-rw-r--r--crates/ide-assists/src/handlers/generate_single_field_struct_from.rs5
-rw-r--r--crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs7
-rw-r--r--crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs181
-rw-r--r--crates/ide-assists/src/tests/generated.rs2
-rw-r--r--crates/ide-assists/src/utils.rs10
-rw-r--r--crates/ide-completion/src/completions/attribute.rs4
-rw-r--r--crates/ide-completion/src/context.rs4
-rw-r--r--crates/ide-completion/src/context/analysis.rs24
-rw-r--r--crates/ide-completion/src/lib.rs1
-rw-r--r--crates/ide-db/src/imports/import_assets.rs4
-rw-r--r--crates/ide-db/src/imports/insert_use.rs20
-rw-r--r--crates/ide-diagnostics/src/handlers/inactive_code.rs4
-rw-r--r--crates/ide/src/expand_macro.rs3
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html6
-rw-r--r--crates/parser/src/grammar/attributes.rs113
-rw-r--r--crates/parser/src/syntax_kind/generated.rs30
-rw-r--r--crates/parser/test_data/generated/runner.rs20
-rw-r--r--crates/parser/test_data/parser/err/0005_attribute_recover.rast4
-rw-r--r--crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast13
-rw-r--r--crates/parser/test_data/parser/err/0033_match_arms_outer_attrs.rast13
-rw-r--r--crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rast19
-rw-r--r--crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rs1
-rw-r--r--crates/parser/test_data/parser/inline/err/meta_recovery.rast18
-rw-r--r--crates/parser/test_data/parser/inline/ok/arg_with_attr.rast2
-rw-r--r--crates/parser/test_data/parser/inline/ok/array_attrs.rast13
-rw-r--r--crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast2
-rw-r--r--crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast10
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_attr.rast63
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_attr.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rast33
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rast17
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_meta.rast30
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_meta.rs2
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rast25
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rs2
-rw-r--r--crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast4
-rw-r--r--crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast6
-rw-r--r--crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast65
-rw-r--r--crates/parser/test_data/parser/inline/ok/metas.rast274
-rw-r--r--crates/parser/test_data/parser/inline/ok/param_outer_arg.rast2
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_field_attrs.rast2
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast13
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_pat_field.rast16
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast16
-rw-r--r--crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast2
-rw-r--r--crates/parser/test_data/parser/inline/ok/tuple_attrs.rast13
-rw-r--r--crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast2
-rw-r--r--crates/parser/test_data/parser/ok/0006_inner_attributes.rast20
-rw-r--r--crates/parser/test_data/parser/ok/0008_mod_item.rast2
-rw-r--r--crates/parser/test_data/parser/ok/0011_outer_attribute.rast17
-rw-r--r--crates/parser/test_data/parser/ok/0017_attr_trailing_comma.rast2
-rw-r--r--crates/parser/test_data/parser/ok/0035_weird_exprs.rast10
-rw-r--r--crates/parser/test_data/parser/ok/0044_let_attrs.rast13
-rw-r--r--crates/parser/test_data/parser/ok/0045_block_attrs.rast12
-rw-r--r--crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast2
-rw-r--r--crates/parser/test_data/parser/ok/0051_parameter_attrs.rast28
-rw-r--r--crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast2
-rw-r--r--crates/parser/test_data/parser/ok/0062_macro_2.0.rast2
-rw-r--r--crates/parser/test_data/parser/ok/0063_variadic_fun.rast13
-rw-r--r--crates/parser/test_data/parser/ok/0070_expr_attr_placement.rast4
-rw-r--r--crates/parser/test_data/parser/ok/0071_stmt_attr_placement.rast6
-rw-r--r--crates/rust-analyzer/src/target_spec.rs16
-rw-r--r--crates/syntax/Cargo.toml1
-rw-r--r--crates/syntax/rust.ungram36
-rw-r--r--crates/syntax/src/ast.rs6
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs565
-rw-r--r--crates/syntax/src/ast/make.rs12
-rw-r--r--crates/syntax/src/ast/node_ext.rs120
-rw-r--r--crates/syntax/src/ast/syntax_factory/constructors.rs31
-rw-r--r--crates/syntax/src/ast/traits.rs3
-rw-r--r--crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast8
-rw-r--r--xtask/src/codegen/grammar.rs28
-rw-r--r--xtask/src/codegen/grammar/ast_src.rs17
98 files changed, 1957 insertions, 1344 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 770f8cb940..da530b3a93 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -234,7 +234,6 @@ dependencies = [
"intern",
"oorandom",
"rustc-hash 2.1.1",
- "span",
"syntax",
"syntax-bridge",
"tracing",
@@ -846,7 +845,6 @@ dependencies = [
name = "hir-expand"
version = "0.0.0"
dependencies = [
- "arrayvec",
"base-db",
"cfg",
"cov-mark",
@@ -2733,6 +2731,7 @@ dependencies = [
"rustc-hash 2.1.1",
"rustc-literal-escaper 0.0.4",
"rustc_apfloat",
+ "smallvec",
"smol_str 0.3.2",
"stdx",
"test-utils",
diff --git a/crates/cfg/Cargo.toml b/crates/cfg/Cargo.toml
index cf2a7607b0..15de1f3293 100644
--- a/crates/cfg/Cargo.toml
+++ b/crates/cfg/Cargo.toml
@@ -19,7 +19,6 @@ tracing.workspace = true
# locals deps
tt = { workspace = true, optional = true }
syntax = { workspace = true, optional = true }
-span = { path = "../span", version = "0.0", optional = true }
intern.workspace = true
[dev-dependencies]
@@ -36,7 +35,7 @@ cfg = { path = ".", default-features = false, features = ["tt"] }
[features]
default = []
-syntax = ["dep:syntax", "dep:span"]
+syntax = ["dep:syntax"]
tt = ["dep:tt"]
in-rust-tree = []
diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs
index d253f6f492..7af3ed5dc9 100644
--- a/crates/cfg/src/cfg_expr.rs
+++ b/crates/cfg/src/cfg_expr.rs
@@ -106,10 +106,54 @@ impl CfgExpr {
}
#[cfg(feature = "syntax")]
- pub fn parse_from_ast(
- ast: &mut std::iter::Peekable<syntax::ast::TokenTreeChildren>,
- ) -> CfgExpr {
- next_cfg_expr_from_ast(ast).unwrap_or(CfgExpr::Invalid)
+ pub fn parse_from_ast(ast: syntax::ast::CfgPredicate) -> CfgExpr {
+ use intern::sym;
+ use syntax::ast::{self, AstToken};
+
+ match ast {
+ ast::CfgPredicate::CfgAtom(atom) => {
+ let atom = match atom.key() {
+ Some(ast::CfgAtomKey::True) => CfgAtom::Flag(sym::true_),
+ Some(ast::CfgAtomKey::False) => CfgAtom::Flag(sym::false_),
+ Some(ast::CfgAtomKey::Ident(key)) => {
+ let key = Symbol::intern(key.text());
+ match atom.string_token().and_then(ast::String::cast) {
+ Some(value) => {
+ if let Ok(value) = value.value() {
+ CfgAtom::KeyValue { key, value: Symbol::intern(&value) }
+ } else {
+ return CfgExpr::Invalid;
+ }
+ }
+ None => CfgAtom::Flag(key),
+ }
+ }
+ None => return CfgExpr::Invalid,
+ };
+ CfgExpr::Atom(atom)
+ }
+ ast::CfgPredicate::CfgComposite(composite) => {
+ let Some(keyword) = composite.keyword() else {
+ return CfgExpr::Invalid;
+ };
+ match keyword.text() {
+ "all" => CfgExpr::All(
+ composite.cfg_predicates().map(CfgExpr::parse_from_ast).collect(),
+ ),
+ "any" => CfgExpr::Any(
+ composite.cfg_predicates().map(CfgExpr::parse_from_ast).collect(),
+ ),
+ "not" => {
+ let mut inner = composite.cfg_predicates();
+ let (Some(inner), None) = (inner.next(), inner.next()) else {
+ return CfgExpr::Invalid;
+ };
+ CfgExpr::Not(Box::new(CfgExpr::parse_from_ast(inner)))
+ }
+ _ => CfgExpr::Invalid,
+ }
+ }
+ }
}
/// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
@@ -128,65 +172,6 @@ impl CfgExpr {
}
}
-#[cfg(feature = "syntax")]
-fn next_cfg_expr_from_ast(
- it: &mut std::iter::Peekable<syntax::ast::TokenTreeChildren>,
-) -> Option<CfgExpr> {
- use intern::sym;
- use syntax::{NodeOrToken, SyntaxKind, T, ast};
-
- let name = match it.next() {
- None => return None,
- Some(NodeOrToken::Token(ident)) if ident.kind().is_any_identifier() => {
- Symbol::intern(ident.text())
- }
- Some(_) => return Some(CfgExpr::Invalid),
- };
-
- let ret = match it.peek() {
- Some(NodeOrToken::Token(eq)) if eq.kind() == T![=] => {
- it.next();
- if let Some(NodeOrToken::Token(literal)) = it.peek()
- && matches!(literal.kind(), SyntaxKind::STRING)
- {
- let dummy_span = span::Span {
- range: span::TextRange::empty(span::TextSize::new(0)),
- anchor: span::SpanAnchor {
- file_id: span::EditionedFileId::from_raw(0),
- ast_id: span::FIXUP_ERASED_FILE_AST_ID_MARKER,
- },
- ctx: span::SyntaxContext::root(span::Edition::Edition2015),
- };
- let literal =
- Symbol::intern(tt::token_to_literal(literal.text(), dummy_span).text());
- it.next();
- CfgAtom::KeyValue { key: name, value: literal.clone() }.into()
- } else {
- return Some(CfgExpr::Invalid);
- }
- }
- Some(NodeOrToken::Node(subtree)) => {
- let mut subtree_iter = ast::TokenTreeChildren::new(subtree).peekable();
- it.next();
- let mut subs = std::iter::from_fn(|| next_cfg_expr_from_ast(&mut subtree_iter));
- match name {
- s if s == sym::all => CfgExpr::All(subs.collect()),
- s if s == sym::any => CfgExpr::Any(subs.collect()),
- s if s == sym::not => {
- CfgExpr::Not(Box::new(subs.next().unwrap_or(CfgExpr::Invalid)))
- }
- _ => CfgExpr::Invalid,
- }
- }
- _ => CfgAtom::Flag(name).into(),
- };
-
- // Eat comma separator
- while it.next().is_some_and(|it| it.as_token().is_none_or(|it| it.kind() != T![,])) {}
-
- Some(ret)
-}
-
#[cfg(feature = "tt")]
fn next_cfg_expr(it: &mut tt::iter::TtIter<'_>) -> Option<CfgExpr> {
use intern::sym;
diff --git a/crates/cfg/src/tests.rs b/crates/cfg/src/tests.rs
index 52c581dbbd..bfc9220a05 100644
--- a/crates/cfg/src/tests.rs
+++ b/crates/cfg/src/tests.rs
@@ -1,10 +1,7 @@
use arbitrary::{Arbitrary, Unstructured};
use expect_test::{Expect, expect};
use intern::Symbol;
-use syntax::{
- AstNode, Edition,
- ast::{self, TokenTreeChildren},
-};
+use syntax::{AstNode, Edition, ast};
use syntax_bridge::{
DocCommentDesugarMode,
dummy_test_span_utils::{DUMMY, DummyTestSpanMap},
@@ -14,32 +11,32 @@ use syntax_bridge::{
use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
#[track_caller]
-fn parse_ast_cfg(tt: &ast::TokenTree) -> CfgExpr {
- CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(tt).peekable())
+fn parse_ast_cfg(pred: ast::CfgPredicate) -> CfgExpr {
+ CfgExpr::parse_from_ast(pred)
}
#[track_caller]
fn assert_parse_result(input: &str, expected: CfgExpr) {
- let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
- let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+ let source_file = ast::SourceFile::parse(input, Edition::CURRENT).syntax_node();
+ let pred_ast = source_file.descendants().find_map(ast::CfgPredicate::cast).unwrap();
let tt = syntax_node_to_token_tree(
- tt_ast.syntax(),
+ pred_ast.syntax(),
DummyTestSpanMap,
DUMMY,
DocCommentDesugarMode::ProcMacro,
);
let cfg = CfgExpr::parse(&tt);
assert_eq!(cfg, expected);
- let cfg = parse_ast_cfg(&tt_ast);
+ let cfg = parse_ast_cfg(pred_ast);
assert_eq!(cfg, expected);
}
#[track_caller]
fn check_dnf(input: &str, expect: Expect) {
let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
- let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+ let pred_ast = source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap();
let tt = syntax_node_to_token_tree(
- tt_ast.syntax(),
+ pred_ast.syntax(),
DummyTestSpanMap,
DUMMY,
DocCommentDesugarMode::ProcMacro,
@@ -47,7 +44,7 @@ fn check_dnf(input: &str, expect: Expect) {
let cfg = CfgExpr::parse(&tt);
let actual = format!("#![cfg({})]", DnfExpr::new(&cfg));
expect.assert_eq(&actual);
- let cfg = parse_ast_cfg(&tt_ast);
+ let cfg = parse_ast_cfg(pred_ast);
let actual = format!("#![cfg({})]", DnfExpr::new(&cfg));
expect.assert_eq(&actual);
}
@@ -55,9 +52,9 @@ fn check_dnf(input: &str, expect: Expect) {
#[track_caller]
fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
- let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+ let pred_ast = source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap();
let tt = syntax_node_to_token_tree(
- tt_ast.syntax(),
+ pred_ast.syntax(),
DummyTestSpanMap,
DUMMY,
DocCommentDesugarMode::ProcMacro,
@@ -66,7 +63,7 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
let dnf = DnfExpr::new(&cfg);
let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
expect.assert_eq(&why_inactive);
- let cfg = parse_ast_cfg(&tt_ast);
+ let cfg = parse_ast_cfg(pred_ast);
let dnf = DnfExpr::new(&cfg);
let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
expect.assert_eq(&why_inactive);
@@ -75,9 +72,9 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
#[track_caller]
fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
- let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+ let pred_ast = source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap();
let tt = syntax_node_to_token_tree(
- tt_ast.syntax(),
+ pred_ast.syntax(),
DummyTestSpanMap,
DUMMY,
DocCommentDesugarMode::ProcMacro,
@@ -86,7 +83,7 @@ fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
let dnf = DnfExpr::new(&cfg);
let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>();
assert_eq!(hints, expected_hints);
- let cfg = parse_ast_cfg(&tt_ast);
+ let cfg = parse_ast_cfg(pred_ast);
let dnf = DnfExpr::new(&cfg);
let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>();
assert_eq!(hints, expected_hints);
@@ -119,20 +116,6 @@ fn test_cfg_expr_parser() {
.into_boxed_slice(),
),
);
-
- assert_parse_result(
- r#"#![cfg(any(not(), all(), , bar = "baz",))]"#,
- CfgExpr::Any(
- vec![
- CfgExpr::Not(Box::new(CfgExpr::Invalid)),
- CfgExpr::All(Box::new([])),
- CfgExpr::Invalid,
- CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") }
- .into(),
- ]
- .into_boxed_slice(),
- ),
- );
}
#[test]
diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs
index dddfe8cefd..b560d08492 100644
--- a/crates/hir-def/src/attrs.rs
+++ b/crates/hir-def/src/attrs.rs
@@ -22,7 +22,7 @@ use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_expand::{
InFile, Lookup,
- attrs::{Meta, expand_cfg_attr},
+ attrs::{AstKeyValueMetaExt, AstPathExt, expand_cfg_attr},
};
use intern::Symbol;
use itertools::Itertools;
@@ -128,63 +128,89 @@ fn extract_rustc_skip_during_method_dispatch(attr_flags: &mut AttrFlags, tt: ast
}
#[inline]
-fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow<Infallible> {
+fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow<Infallible> {
match attr {
- Meta::NamedKeyValue { name: Some(name), value, .. } => match name.text() {
- "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
- "ignore" => attr_flags.insert(AttrFlags::IS_IGNORE),
- "lang" => attr_flags.insert(AttrFlags::LANG_ITEM),
- "path" => attr_flags.insert(AttrFlags::HAS_PATH),
- "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE),
- "export_name" => {
- if let Some(value) = value
- && let Some(value) = ast::String::cast(value)
- && let Ok(value) = value.value()
- && *value == *"main"
- {
- attr_flags.insert(AttrFlags::IS_EXPORT_NAME_MAIN);
- }
- }
- _ => {}
- },
- Meta::TokenTree { path, tt } => match path.segments.len() {
- 1 => match path.segments[0].text() {
+ ast::Meta::CfgMeta(_) => attr_flags.insert(AttrFlags::HAS_CFG),
+ ast::Meta::KeyValueMeta(attr) => {
+ let Some(key) = attr.path().as_one_segment() else { return ControlFlow::Continue(()) };
+ match &*key {
"deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
- "cfg" => attr_flags.insert(AttrFlags::HAS_CFG),
- "doc" => extract_doc_tt_attr(attr_flags, tt),
- "repr" => attr_flags.insert(AttrFlags::HAS_REPR),
- "target_feature" => attr_flags.insert(AttrFlags::HAS_TARGET_FEATURE),
- "proc_macro_derive" | "rustc_builtin_macro" => {
- attr_flags.insert(AttrFlags::IS_DERIVE_OR_BUILTIN_MACRO)
- }
+ "ignore" => attr_flags.insert(AttrFlags::IS_IGNORE),
+ "lang" => attr_flags.insert(AttrFlags::LANG_ITEM),
+ "path" => attr_flags.insert(AttrFlags::HAS_PATH),
"unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE),
- "rustc_layout_scalar_valid_range_start" | "rustc_layout_scalar_valid_range_end" => {
- attr_flags.insert(AttrFlags::RUSTC_LAYOUT_SCALAR_VALID_RANGE)
- }
- "rustc_legacy_const_generics" => {
- attr_flags.insert(AttrFlags::HAS_LEGACY_CONST_GENERICS)
- }
- "rustc_skip_during_method_dispatch" => {
- extract_rustc_skip_during_method_dispatch(attr_flags, tt)
- }
- "rustc_deprecated_safe_2024" => {
- attr_flags.insert(AttrFlags::RUSTC_DEPRECATED_SAFE_2024)
+ "export_name" => {
+ if let Some(value) = attr.value_string()
+ && *value == *"main"
+ {
+ attr_flags.insert(AttrFlags::IS_EXPORT_NAME_MAIN);
+ }
}
_ => {}
- },
- 2 => match path.segments[0].text() {
- "rust_analyzer" => match path.segments[1].text() {
- "completions" => extract_ra_completions(attr_flags, tt),
- "macro_style" => extract_ra_macro_style(attr_flags, tt),
+ }
+ }
+ ast::Meta::TokenTreeMeta(attr) => {
+ let (Some((first_segment, second_segment)), Some(tt)) =
+ (attr.path().as_up_to_two_segment(), attr.token_tree())
+ else {
+ return ControlFlow::Continue(());
+ };
+ match second_segment {
+ None => match &*first_segment {
+ "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
+ "doc" => extract_doc_tt_attr(attr_flags, tt),
+ "repr" => attr_flags.insert(AttrFlags::HAS_REPR),
+ "target_feature" => attr_flags.insert(AttrFlags::HAS_TARGET_FEATURE),
+ "proc_macro_derive" | "rustc_builtin_macro" => {
+ attr_flags.insert(AttrFlags::IS_DERIVE_OR_BUILTIN_MACRO)
+ }
+ "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE),
+ "rustc_layout_scalar_valid_range_start"
+ | "rustc_layout_scalar_valid_range_end" => {
+ attr_flags.insert(AttrFlags::RUSTC_LAYOUT_SCALAR_VALID_RANGE)
+ }
+ "rustc_legacy_const_generics" => {
+ attr_flags.insert(AttrFlags::HAS_LEGACY_CONST_GENERICS)
+ }
+ "rustc_skip_during_method_dispatch" => {
+ extract_rustc_skip_during_method_dispatch(attr_flags, tt)
+ }
+ "rustc_deprecated_safe_2024" => {
+ attr_flags.insert(AttrFlags::RUSTC_DEPRECATED_SAFE_2024)
+ }
_ => {}
},
- _ => {}
- },
- _ => {}
- },
- Meta::Path { path } => {
- match path.segments.len() {
- 1 => match path.segments[0].text() {
+ Some(second_segment) => match &*first_segment {
+ "rust_analyzer" => match &*second_segment {
+ "completions" => extract_ra_completions(attr_flags, tt),
+ "macro_style" => extract_ra_macro_style(attr_flags, tt),
+ _ => {}
+ },
+ _ => {}
+ },
+ }
+ }
+ ast::Meta::PathMeta(attr) => {
+ let is_test = attr.path().is_some_and(|path| {
+ let Some(segment1) = (|| path.segment()?.name_ref())() else { return false };
+ let segment2 = path.qualifier();
+ let segment3 = segment2.as_ref().and_then(|it| it.qualifier());
+ let segment4 = segment3.as_ref().and_then(|it| it.qualifier());
+ let segment3 = segment3.and_then(|it| it.segment()?.name_ref());
+ let segment4 = segment4.and_then(|it| it.segment()?.name_ref());
+ segment1.text() == "test"
+ && segment3.is_none_or(|it| it.text() == "prelude")
+ && segment4.is_none_or(|it| it.text() == "core")
+ });
+ if is_test {
+ attr_flags.insert(AttrFlags::IS_TEST);
+ }
+
+ let Some((first_segment, second_segment)) = attr.path().as_up_to_two_segment() else {
+ return ControlFlow::Continue(());
+ };
+ match second_segment {
+ None => match &*first_segment {
"rustc_has_incoherent_inherent_impls" => {
attr_flags.insert(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS)
}
@@ -228,18 +254,13 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow<Infal
}
_ => {}
},
- 2 => match path.segments[0].text() {
- "rust_analyzer" => match path.segments[1].text() {
+ Some(second_segment) => match &*first_segment {
+ "rust_analyzer" => match &*second_segment {
"skip" => attr_flags.insert(AttrFlags::RUST_ANALYZER_SKIP),
_ => {}
},
_ => {}
},
- _ => {}
- }
-
- if path.is_test {
- attr_flags.insert(AttrFlags::IS_TEST);
}
}
_ => {}
@@ -420,7 +441,7 @@ fn resolver_for_attr_def_id(db: &dyn DefDatabase, owner: AttrDefId) -> Resolver<
fn collect_attrs<BreakValue>(
db: &dyn DefDatabase,
owner: AttrDefId,
- mut callback: impl FnMut(Meta) -> ControlFlow<BreakValue>,
+ mut callback: impl FnMut(ast::Meta) -> ControlFlow<BreakValue>,
) -> Option<BreakValue> {
let (source, outer_mod_decl, extra_crate_attrs, krate) = attrs_source(db, owner);
let extra_attrs = extra_crate_attrs
@@ -432,7 +453,7 @@ fn collect_attrs<BreakValue>(
expand_cfg_attr(
extra_attrs.chain(ast::attrs_including_inner(&source.value)),
|| cfg_options.get_or_insert_with(|| krate.cfg_options(db)),
- move |meta, _, _, _| callback(meta),
+ move |meta, _| callback(meta),
)
}
@@ -500,9 +521,10 @@ pub struct DeriveInfo {
pub helpers: Box<[Symbol]>,
}
-fn extract_doc_aliases(result: &mut Vec<Symbol>, attr: Meta) -> ControlFlow<Infallible> {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("doc")
+fn extract_doc_aliases(result: &mut Vec<Symbol>, attr: ast::Meta) -> ControlFlow<Infallible> {
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && attr.path().is1("doc")
+ && let Some(tt) = attr.token_tree()
{
for atom in DocAtom::parse(tt) {
match atom {
@@ -519,11 +541,11 @@ fn extract_doc_aliases(result: &mut Vec<Symbol>, attr: Meta) -> ControlFlow<Infa
ControlFlow::Continue(())
}
-fn extract_cfgs(result: &mut Vec<CfgExpr>, attr: Meta) -> ControlFlow<Infallible> {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("cfg")
+fn extract_cfgs(result: &mut Vec<CfgExpr>, attr: ast::Meta) -> ControlFlow<Infallible> {
+ if let ast::Meta::CfgMeta(attr) = attr
+ && let Some(cfg_predicate) = attr.cfg_predicate()
{
- result.push(CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(&tt).peekable()));
+ result.push(CfgExpr::parse_from_ast(cfg_predicate));
}
ControlFlow::Continue(())
}
@@ -554,7 +576,7 @@ impl AttrFlags {
expand_cfg_attr(
field.value.attrs(),
|| cfg_options,
- |attr, _, _, _| match_attr_flags(&mut attr_flags, attr),
+ |attr, _| match_attr_flags(&mut attr_flags, attr),
);
attr_flags
})
@@ -591,7 +613,7 @@ impl AttrFlags {
let lifetimes_source = HasChildSource::<LocalLifetimeParamId>::child_source(&def, db);
for (lifetime_id, lifetime) in lifetimes_source.value.iter() {
let mut attr_flags = AttrFlags::empty();
- expand_cfg_attr(lifetime.attrs(), &mut cfg_options, |attr, _, _, _| {
+ expand_cfg_attr(lifetime.attrs(), &mut cfg_options, |attr, _| {
match_attr_flags(&mut attr_flags, attr)
});
if !attr_flags.is_empty() {
@@ -603,7 +625,7 @@ impl AttrFlags {
HasChildSource::<LocalTypeOrConstParamId>::child_source(&def, db);
for (type_or_const_id, type_or_const) in type_and_consts_source.value.iter() {
let mut attr_flags = AttrFlags::empty();
- expand_cfg_attr(type_or_const.attrs(), &mut cfg_options, |attr, _, _, _| {
+ expand_cfg_attr(type_or_const.attrs(), &mut cfg_options, |attr, _| {
match_attr_flags(&mut attr_flags, attr)
});
if !attr_flags.is_empty() {
@@ -642,11 +664,10 @@ impl AttrFlags {
let result = expand_cfg_attr(
attrs,
|| cfg_options,
- |attr, _, _, _| {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("cfg")
- && let cfg =
- CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(&tt).peekable())
+ |attr, _| {
+ if let ast::Meta::CfgMeta(attr) = attr
+ && let Some(cfg_predicate) = attr.cfg_predicate()
+ && let cfg = CfgExpr::parse_from_ast(cfg_predicate)
&& cfg_options.check(&cfg) == Some(false)
{
ControlFlow::Break(cfg)
@@ -678,10 +699,9 @@ impl AttrFlags {
#[salsa::tracked]
fn lang_item(db: &dyn DefDatabase, owner: AttrDefId) -> Option<Symbol> {
collect_attrs(db, owner, |attr| {
- if let Meta::NamedKeyValue { name: Some(name), value: Some(value), .. } = attr
- && name.text() == "lang"
- && let Some(value) = ast::String::cast(value)
- && let Ok(value) = value.value()
+ if let ast::Meta::KeyValueMeta(attr) = attr
+ && attr.path().is1("lang")
+ && let Some(value) = attr.value_string()
{
ControlFlow::Break(Symbol::intern(&value))
} else {
@@ -704,8 +724,9 @@ impl AttrFlags {
fn repr(db: &dyn DefDatabase, owner: AdtId) -> Option<ReprOptions> {
let mut result = None;
collect_attrs::<Infallible>(db, owner.into(), |attr| {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("repr")
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && attr.path().is1("repr")
+ && let Some(tt) = attr.token_tree()
&& let Some(repr) = parse_repr_tt(&tt)
{
match &mut result {
@@ -726,8 +747,9 @@ impl AttrFlags {
owner: FunctionId,
) -> Option<Box<[u32]>> {
let result = collect_attrs(db, owner.into(), |attr| {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("rustc_legacy_const_generics")
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && attr.path().is1("rustc_legacy_const_generics")
+ && let Some(tt) = attr.token_tree()
{
let result = parse_rustc_legacy_const_generics(tt);
ControlFlow::Break(result)
@@ -750,9 +772,10 @@ impl AttrFlags {
expand_cfg_attr(
extra_crate_attrs.chain(syntax.attrs()),
|| cfg_options.get_or_insert(krate.cfg_options(db)),
- |attr, _, _, _| {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("doc")
+ |attr, _| {
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && attr.path().is1("doc")
+ && let Some(tt) = attr.token_tree()
&& let Some(result) = DocAtom::parse(tt).into_iter().find_map(|atom| {
if let DocAtom::KeyValue { key, value } = atom
&& key == "html_root_url"
@@ -783,8 +806,9 @@ impl AttrFlags {
fn target_features(db: &dyn DefDatabase, owner: FunctionId) -> FxHashSet<Symbol> {
let mut result = FxHashSet::default();
collect_attrs::<Infallible>(db, owner.into(), |attr| {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("target_feature")
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && attr.path().is1("target_feature")
+ && let Some(tt) = attr.token_tree()
{
let mut tt = TokenTreeChildren::new(&tt);
while let Some(NodeOrToken::Token(enable_ident)) = tt.next()
@@ -831,9 +855,11 @@ impl AttrFlags {
) -> RustcLayoutScalarValidRange {
let mut result = RustcLayoutScalarValidRange::default();
collect_attrs::<Infallible>(db, owner.into(), |attr| {
- if let Meta::TokenTree { path, tt } = attr
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && let path = attr.path()
&& (path.is1("rustc_layout_scalar_valid_range_start")
|| path.is1("rustc_layout_scalar_valid_range_end"))
+ && let Some(tt) = attr.token_tree()
&& let tt = TokenTreeChildren::new(&tt)
&& let Ok(NodeOrToken::Token(value)) = Itertools::exactly_one(tt)
&& let Some(value) = ast::IntNumber::cast(value)
@@ -881,7 +907,7 @@ impl AttrFlags {
expand_cfg_attr(
field.value.attrs(),
|| cfg_options,
- |attr, _, _, _| extract_doc_aliases(&mut result, attr),
+ |attr, _| extract_doc_aliases(&mut result, attr),
);
result.into_boxed_slice()
})
@@ -923,7 +949,7 @@ impl AttrFlags {
expand_cfg_attr(
field.value.attrs(),
|| cfg_options,
- |attr, _, _, _| extract_cfgs(&mut result, attr),
+ |attr, _| extract_cfgs(&mut result, attr),
);
match result.len() {
0 => None,
@@ -944,8 +970,9 @@ impl AttrFlags {
#[salsa::tracked]
fn doc_keyword(db: &dyn DefDatabase, owner: ModuleId) -> Option<Symbol> {
collect_attrs(db, AttrDefId::ModuleId(owner), |attr| {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("doc")
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && attr.path().is1("doc")
+ && let Some(tt) = attr.token_tree()
{
for atom in DocAtom::parse(tt) {
if let DocAtom::KeyValue { key, value } = atom
@@ -1015,12 +1042,10 @@ impl AttrFlags {
#[salsa::tracked(returns(ref))]
fn derive_info(db: &dyn DefDatabase, owner: MacroId) -> Option<DeriveInfo> {
collect_attrs(db, owner.into(), |attr| {
- if let Meta::TokenTree { path, tt } = attr
- && path.segments.len() == 1
- && matches!(
- path.segments[0].text(),
- "proc_macro_derive" | "rustc_builtin_macro"
- )
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && (attr.path().is1("proc_macro_derive")
+ || attr.path().is1("rustc_builtin_macro"))
+ && let Some(tt) = attr.token_tree()
&& let mut tt = TokenTreeChildren::new(&tt)
&& let Some(NodeOrToken::Token(trait_name)) = tt.next()
&& trait_name.kind().is_any_identifier()
diff --git a/crates/hir-def/src/attrs/docs.rs b/crates/hir-def/src/attrs/docs.rs
index 8c14808c71..9a715b1968 100644
--- a/crates/hir-def/src/attrs/docs.rs
+++ b/crates/hir-def/src/attrs/docs.rs
@@ -16,7 +16,7 @@ use cfg::CfgOptions;
use either::Either;
use hir_expand::{
AstId, ExpandTo, HirFileId, InFile,
- attrs::{Meta, expand_cfg_attr_with_doc_comments},
+ attrs::{AstPathExt, expand_cfg_attr_with_doc_comments},
mod_path::ModPath,
span_map::SpanMap,
};
@@ -182,8 +182,7 @@ impl Docs {
self.extend_with_doc_str(doc, comment.syntax().text_range().start() + offset, indent);
}
- fn extend_with_doc_attr(&mut self, value: syntax::SyntaxToken, indent: &mut usize) {
- let Some(value) = ast::String::cast(value) else { return };
+ fn extend_with_doc_attr(&mut self, value: ast::String, indent: &mut usize) {
let Some(value_offset) = value.text_range_between_quotes() else { return };
let value_offset = value_offset.start();
let Ok(value) = value.value() else { return };
@@ -423,10 +422,6 @@ fn extend_with_attrs<'a, 'db>(
// Lazily initialised when we first encounter a `#[doc = macro!()]`.
let mut expander: Option<(DocMacroExpander<'db>, DocExprSourceCtx<'db>)> = None;
- // FIXME: `#[cfg_attr(..., doc = macro!())]` skips macro expansion because
- // `top_attr` points to the `cfg_attr` node, not the inner `doc = macro!()`.
- // Fixing this is difficult as we need an `Expr` that doesn't exist here for
- // the ast id and for sanely parsing the macro call.
expand_cfg_attr_with_doc_comments::<_, Infallible>(
AttrDocCommentIter::from_syntax_node(node).filter(|attr| match attr {
Either::Left(attr) => attr.kind().is_inner() == expect_inner_attrs,
@@ -439,46 +434,38 @@ fn extend_with_attrs<'a, 'db>(
|attr| {
match attr {
Either::Right(doc_comment) => result.extend_with_doc_comment(doc_comment, indent),
- Either::Left((attr, _, _, top_attr)) => match attr {
- Meta::NamedKeyValue { name: Some(name), value: Some(value), .. }
- if name.text() == "doc" =>
- {
- result.extend_with_doc_attr(value, indent);
- }
- Meta::NamedKeyValue { name: Some(name), value: None, .. }
- if name.text() == "doc" =>
- {
- // When the doc attribute comes from inside a `cfg_attr`,
- // `top_attr` points to the `cfg_attr(...)` node, not the
- // inner `doc = macro!()`. In that case `top_attr.expr()`
- // would not yield the macro expression we need, so skip
- // expansion (see FIXME above).
- let is_from_cfg_attr =
- top_attr.as_simple_call().is_some_and(|(name, _)| name == "cfg_attr");
- if !is_from_cfg_attr && let Some(expr) = top_attr.expr() {
- let (exp, ctx) = expander.get_or_insert_with(|| {
- let resolver = make_resolver();
- let def_map = resolver.top_level_def_map();
- let recursion_limit = def_map.recursion_limit() as usize;
- (
- DocMacroExpander {
- db,
- krate,
- recursion_depth: 0,
- recursion_limit,
- },
- DocExprSourceCtx {
- resolver,
- file_id,
- ast_id_map: db.ast_id_map(file_id),
- span_map: db.span_map(file_id),
- },
- )
- });
- if let Some(expanded) =
- expand_doc_expr_via_macro_pipeline(exp, ctx, expr)
+ Either::Left((attr, _)) => match attr {
+ ast::Meta::KeyValueMeta(attr) if attr.path().is1("doc") => {
+ if let Some(value) = attr.expr() {
+ if let ast::Expr::Literal(value) = &value
+ && let ast::LiteralKind::String(value) = value.kind()
{
- result.extend_with_unmapped_doc_str(&expanded, indent);
+ result.extend_with_doc_attr(value, indent);
+ } else {
+ let (exp, ctx) = expander.get_or_insert_with(|| {
+ let resolver = make_resolver();
+ let def_map = resolver.top_level_def_map();
+ let recursion_limit = def_map.recursion_limit() as usize;
+ (
+ DocMacroExpander {
+ db,
+ krate,
+ recursion_depth: 0,
+ recursion_limit,
+ },
+ DocExprSourceCtx {
+ resolver,
+ file_id,
+ ast_id_map: db.ast_id_map(file_id),
+ span_map: db.span_map(file_id),
+ },
+ )
+ });
+ if let Some(expanded) =
+ expand_doc_expr_via_macro_pipeline(exp, ctx, value)
+ {
+ result.extend_with_unmapped_doc_str(&expanded, indent);
+ }
}
}
}
diff --git a/crates/hir-def/src/dyn_map.rs b/crates/hir-def/src/dyn_map.rs
index 4308d0ef1c..c38ceccd1f 100644
--- a/crates/hir-def/src/dyn_map.rs
+++ b/crates/hir-def/src/dyn_map.rs
@@ -68,7 +68,7 @@ pub mod keys {
pub const MACRO_CALL: Key<ast::MacroCall, MacroCallId> = Key::new();
pub const ATTR_MACRO_CALL: Key<ast::Item, MacroCallId> = Key::new();
pub const DERIVE_MACRO_CALL: Key<
- ast::Attr,
+ ast::Meta,
(
AttrId,
/* derive() */ MacroCallId,
diff --git a/crates/hir-def/src/item_tree/attrs.rs b/crates/hir-def/src/item_tree/attrs.rs
index 7907611284..867d813e3f 100644
--- a/crates/hir-def/src/item_tree/attrs.rs
+++ b/crates/hir-def/src/item_tree/attrs.rs
@@ -13,12 +13,12 @@ use std::{
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_expand::{
- attrs::{Attr, AttrId, AttrInput, Meta, collect_item_tree_attrs},
+ attrs::{Attr, AttrId, AttrInput, collect_item_tree_attrs},
mod_path::ModPath,
name::Name,
};
use intern::{Interned, Symbol, sym};
-use syntax::{AstNode, T, ast};
+use syntax::{AstNode, ast};
use syntax_bridge::DocCommentDesugarMode;
use tt::token_to_literal;
@@ -51,58 +51,62 @@ impl AttrsOrCfg {
S: syntax_bridge::SpanMapper + Copy,
{
let mut attrs = Vec::new();
- let result =
- collect_item_tree_attrs::<Infallible>(owner, cfg_options, |meta, container, _, _| {
- // NOTE: We cannot early return from this function, *every* attribute must be pushed, otherwise we'll mess the `AttrId`
- // tracking.
- let (span, path_range, input) = match meta {
- Meta::NamedKeyValue { path_range, name: _, value } => {
- let span = span_map.span_for(path_range);
- let input = value.map(|value| {
- Box::new(AttrInput::Literal(token_to_literal(
- value.text(),
- span_map.span_for(value.text_range()),
- )))
- });
- (span, path_range, input)
- }
- Meta::TokenTree { path, tt } => {
- let span = span_map.span_for(path.range);
- let tt = syntax_bridge::syntax_node_to_token_tree(
- tt.syntax(),
- span_map,
- span,
- DocCommentDesugarMode::ProcMacro,
- );
- let input = Some(Box::new(AttrInput::TokenTree(tt)));
- (span, path.range, input)
- }
- Meta::Path { path } => {
- let span = span_map.span_for(path.range);
- (span, path.range, None)
- }
- };
+ let result = collect_item_tree_attrs::<Infallible>(owner, cfg_options, |meta, _| {
+ // NOTE: We cannot early return from this function, *every* attribute must be pushed, otherwise we'll mess the `AttrId`
+ // tracking.
+ let path = meta.path();
+ let path_range = path
+ .as_ref()
+ .map(|path| path.syntax().text_range())
+ .unwrap_or_else(|| meta.syntax().text_range());
+ let (span, input) = match &meta {
+ ast::Meta::KeyValueMeta(meta) => {
+ let span = span_map.span_for(path_range);
+ let input = meta.expr().and_then(|value| {
+ if let ast::Expr::Literal(value) = value {
+ Some(Box::new(AttrInput::Literal(token_to_literal(
+ value.token().text(),
+ span_map.span_for(value.syntax().text_range()),
+ ))))
+ } else {
+ None
+ }
+ });
+ (span, input)
+ }
+ ast::Meta::TokenTreeMeta(meta) => {
+ let span = span_map.span_for(path_range);
+ let tt = syntax_bridge::syntax_node_to_token_tree(
+ &meta
+ .token_tree()
+ .map(|it| it.syntax().clone())
+ .unwrap_or_else(|| meta.syntax().clone()),
+ span_map,
+ span,
+ DocCommentDesugarMode::ProcMacro,
+ );
+ let input = Some(Box::new(AttrInput::TokenTree(tt)));
+ (span, input)
+ }
+ ast::Meta::PathMeta(_) => {
+ let span = span_map.span_for(path_range);
+ (span, None)
+ }
+ ast::Meta::CfgMeta(_) | ast::Meta::CfgAttrMeta(_) | ast::Meta::UnsafeMeta(_) => {
+ unreachable!(
+ "`cfg`, `cfg_attr` and `unsafe(...)` are handled in `collect_item_tree_attrs()`"
+ )
+ }
+ };
- let path = container.token_at_offset(path_range.start()).right_biased().and_then(
- |first_path_token| {
- let is_abs = matches!(first_path_token.kind(), T![:] | T![::]);
- let segments =
- std::iter::successors(Some(first_path_token), |it| it.next_token())
- .take_while(|it| it.text_range().end() <= path_range.end())
- .filter(|it| it.kind().is_any_identifier());
- ModPath::from_tokens(
- db,
- &mut |range| span_map.span_for(range).ctx,
- is_abs,
- segments,
- )
- },
- );
- let path = path.unwrap_or_else(|| Name::missing().into());
-
- attrs.push(Attr { path: Interned::new(path), input, ctxt: span.ctx });
- ControlFlow::Continue(())
+ let path = path.and_then(|path| {
+ ModPath::from_src(db, path, &mut |range| span_map.span_for(range).ctx)
});
+ let path = path.unwrap_or_else(|| Name::missing().into());
+
+ attrs.push(Attr { path: Interned::new(path), input, ctxt: span.ctx });
+ ControlFlow::Continue(())
+ });
let attrs = AttrsOwned(attrs.into_boxed_slice());
match result {
Some(Either::Right(cfg)) => AttrsOrCfg::CfgDisabled(Box::new((cfg, attrs))),
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 7b5d0103e6..e75c96b630 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -1198,7 +1198,7 @@ m! { hello::world }
macro_rules! m {
($m:meta) => ( #[$m] fn bar() {} )
}
-#[cfg(target_os = "windows")] fn bar() {}
+#[cfg (target_os = "windows")] fn bar() {}
#[hello::world] fn bar() {}
"#]],
);
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index ddabb50251..cac248f47f 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -205,7 +205,7 @@ impl Clone for D3DVSHADERCAPS2_0 {
*self
}
}
-#[cfg(feature = "impl-default")] impl Default for D3DVSHADERCAPS2_0 {
+#[cfg (feature = "impl-default")] impl Default for D3DVSHADERCAPS2_0 {
#[inline] fn default() -> D3DVSHADERCAPS2_0 {
unsafe {
$crate::_core::mem::zeroed()
@@ -215,7 +215,7 @@ impl Clone for D3DVSHADERCAPS2_0 {
#[repr(C)]
#[derive(Copy)]
-#[cfg_attr(target_arch = "x86", repr(packed))] pub struct D3DCONTENTPROTECTIONCAPS {
+#[cfg_attr (target_arch = "x86", repr(packed))] pub struct D3DCONTENTPROTECTIONCAPS {
pub Caps: u8,
}
impl Clone for D3DCONTENTPROTECTIONCAPS {
@@ -223,7 +223,7 @@ impl Clone for D3DCONTENTPROTECTIONCAPS {
*self
}
}
-#[cfg(feature = "impl-default")] impl Default for D3DCONTENTPROTECTIONCAPS {
+#[cfg (feature = "impl-default")] impl Default for D3DCONTENTPROTECTIONCAPS {
#[inline] fn default() -> D3DCONTENTPROTECTIONCAPS {
unsafe {
$crate::_core::mem::zeroed()
@@ -1001,8 +1001,8 @@ macro_rules! with_std {
($($i:item)*) => ($(#[cfg(feature = "std")]$i)*)
}
-#[cfg(feature = "std")] mod m;
-#[cfg(feature = "std")] mod f;
+#[cfg (feature = "std")] mod m;
+#[cfg (feature = "std")] mod f;
"#]],
)
}
diff --git a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
index bf04a500a5..8c91cf6793 100644
--- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
+++ b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
@@ -55,8 +55,8 @@ mod foo {
# ![doc = "123..."]
# ![attr2]
# ![attr3]
- #[cfg_attr(true , cfg(false ))] fn foo() {}
- #[cfg(true )] fn bar() {}
+ #[cfg_attr (true , cfg (false ))] fn foo() {}
+ #[cfg (true )] fn bar() {}
}"##]],
);
}
diff --git a/crates/hir-expand/Cargo.toml b/crates/hir-expand/Cargo.toml
index 4fa476afb6..43b0bea891 100644
--- a/crates/hir-expand/Cargo.toml
+++ b/crates/hir-expand/Cargo.toml
@@ -23,7 +23,6 @@ triomphe.workspace = true
query-group.workspace = true
salsa.workspace = true
salsa-macros.workspace = true
-arrayvec.workspace = true
thin-vec.workspace = true
# local deps
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index e3f10b2129..49baecb90c 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -4,20 +4,8 @@
//! [`expand_cfg_attr_with_doc_comments()`]. It is used to implement all attribute lowering
//! in r-a. Its basic job is to list attributes; however, attributes do not necessarily map
//! into [`ast::Attr`], because `cfg_attr` can map to zero, one, or more attributes
-//! (`#[cfg_attr(predicate, attr1, attr2, ...)]`). To bridge this gap, this module defines
-//! [`Meta`], which represents a desugared attribute. Various bits of r-a need different
-//! things from [`Meta`], therefore it contains many parts. The basic idea is:
-//!
-//! - There are three kinds of attributes, `path = value`, `path`, and `path(token_tree)`.
-//! - Most bits of rust-analyzer only need to deal with some paths. Therefore, we keep
-//! the path only if it has up to 2 segments, or one segment for `path = value`.
-//! We also only keep the value in `path = value` if it is a literal. However, we always
-//! save the all relevant ranges of attributes (the path range, and the full attribute range)
-//! for parts of r-a (e.g. name resolution) that need a faithful representation of the
-//! attribute.
-//!
-//! [`expand_cfg_attr()`] expands `cfg_attr`s as it goes (as its name implies), to list
-//! all attributes.
+//! (`#[cfg_attr(predicate, attr1, attr2, ...)]`). [`expand_cfg_attr()`] expands `cfg_attr`s
+//! as it goes (as its name implies), to list all attributes.
//!
//! Another thing to note is that we need to be able to map an attribute back to a range
//! (for diagnostic purposes etc.). This is only ever needed for attributes that participate
@@ -26,26 +14,18 @@
//! place (here) and one function ([`is_item_tree_filtered_attr()`]) that decides whether
//! an attribute participate in name resolution.
-use std::{
- borrow::Cow, cell::OnceCell, convert::Infallible, fmt, iter::Peekable, ops::ControlFlow,
-};
+use std::{borrow::Cow, cell::OnceCell, convert::Infallible, fmt, ops::ControlFlow};
-use ::tt::{TextRange, TextSize};
-use arrayvec::ArrayVec;
+use ::tt::TextRange;
use base_db::Crate;
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use intern::Interned;
use itertools::Itertools;
use mbe::{DelimiterKind, Punct};
-use parser::T;
use smallvec::SmallVec;
use span::{RealSpanMap, Span, SyntaxContext};
-use syntax::{
- AstNode, NodeOrToken, SyntaxNode, SyntaxToken,
- ast::{self, TokenTreeChildren},
- unescape,
-};
+use syntax::{AstNode, SmolStr, ast, unescape};
use syntax_bridge::DocCommentDesugarMode;
use crate::{
@@ -56,207 +36,75 @@ use crate::{
tt::{self, TopSubtree},
};
-#[derive(Debug)]
-pub struct AttrPath {
- /// This can be empty if the path is not of 1 or 2 segments exactly.
- pub segments: ArrayVec<SyntaxToken, 2>,
- pub range: TextRange,
- // FIXME: This shouldn't be textual, `#[test]` needs name resolution.
- // And if textual, it shouldn't be here, it should be in hir-def/src/attrs.rs. But some macros
- // fully qualify `test` as `core::prelude::vX::test`, and this is more than 2 segments, so hir-def
- // attrs can't find it. But this will mean we have to push every up-to-4-segments path, which
- // may impact perf. So it was easier to just hack it here.
- pub is_test: bool,
+pub trait AstPathExt {
+ fn is1(&self, segment: &str) -> bool;
+
+ fn as_one_segment(&self) -> Option<SmolStr>;
+
+ fn as_up_to_two_segment(&self) -> Option<(SmolStr, Option<SmolStr>)>;
}
-impl AttrPath {
- #[inline]
- fn extract(path: &ast::Path) -> Self {
- let mut is_test = false;
- let segments = (|| {
- let mut segments = ArrayVec::new();
- let segment2 = path.segment()?.name_ref()?.syntax().first_token()?;
- if segment2.text() == "test" {
- // `#[test]` or `#[core::prelude::vX::test]`.
- is_test = true;
- }
- let segment1 = path.qualifier();
- if let Some(segment1) = segment1 {
- if segment1.qualifier().is_some() {
- None
- } else {
- let segment1 = segment1.segment()?.name_ref()?.syntax().first_token()?;
- segments.push(segment1);
- segments.push(segment2);
- Some(segments)
- }
- } else {
- segments.push(segment2);
- Some(segments)
- }
- })();
- AttrPath {
- segments: segments.unwrap_or(ArrayVec::new()),
- range: path.syntax().text_range(),
- is_test,
- }
+impl AstPathExt for ast::Path {
+ fn is1(&self, segment: &str) -> bool {
+ self.as_one_segment().is_some_and(|it| it == segment)
}
- #[inline]
- pub fn is1(&self, segment: &str) -> bool {
- self.segments.len() == 1 && self.segments[0].text() == segment
+ fn as_one_segment(&self) -> Option<SmolStr> {
+ Some(self.as_single_name_ref()?.text().into())
}
-}
-#[derive(Debug)]
-pub enum Meta {
- /// `name` is `None` if not a single token. `value` is a literal or `None`.
- NamedKeyValue {
- path_range: TextRange,
- name: Option<SyntaxToken>,
- value: Option<SyntaxToken>,
- },
- TokenTree {
- path: AttrPath,
- tt: ast::TokenTree,
- },
- Path {
- path: AttrPath,
- },
+ fn as_up_to_two_segment(&self) -> Option<(SmolStr, Option<SmolStr>)> {
+ let parent = self.qualifier().as_one_segment();
+ let this = self.segment()?.name_ref()?.text().into();
+ if let Some(parent) = parent { Some((parent, Some(this))) } else { Some((this, None)) }
+ }
}
-impl Meta {
- #[inline]
- pub fn path_range(&self) -> TextRange {
- match self {
- Meta::NamedKeyValue { path_range, .. } => *path_range,
- Meta::TokenTree { path, .. } | Meta::Path { path } => path.range,
- }
+impl AstPathExt for Option<ast::Path> {
+ fn is1(&self, segment: &str) -> bool {
+ self.as_ref().is_some_and(|it| it.is1(segment))
}
- fn extract(iter: &mut Peekable<TokenTreeChildren>) -> Option<(Self, TextSize)> {
- let mut start_offset = None;
- if let Some(NodeOrToken::Token(colon1)) = iter.peek()
- && colon1.kind() == T![:]
- {
- start_offset = Some(colon1.text_range().start());
- iter.next();
- iter.next_if(|it| it.as_token().is_some_and(|it| it.kind() == T![:]));
- }
- let first_segment = iter
- .next_if(|it| it.as_token().is_some_and(|it| it.kind().is_any_identifier()))?
- .into_token()?;
- let mut is_test = first_segment.text() == "test";
- let start_offset = start_offset.unwrap_or_else(|| first_segment.text_range().start());
-
- let mut segments_len = 1;
- let mut second_segment = None;
- let mut path_range = first_segment.text_range();
- while iter.peek().and_then(NodeOrToken::as_token).is_some_and(|it| it.kind() == T![:])
- && let _ = iter.next()
- && iter.peek().and_then(NodeOrToken::as_token).is_some_and(|it| it.kind() == T![:])
- && let _ = iter.next()
- && let Some(NodeOrToken::Token(segment)) = iter.peek()
- && segment.kind().is_any_identifier()
- {
- segments_len += 1;
- is_test = segment.text() == "test";
- second_segment = Some(segment.clone());
- path_range = TextRange::new(path_range.start(), segment.text_range().end());
- iter.next();
- }
+ fn as_one_segment(&self) -> Option<SmolStr> {
+ self.as_ref().and_then(|it| it.as_one_segment())
+ }
- let segments = |first, second| {
- let mut segments = ArrayVec::new();
- if segments_len <= 2 {
- segments.push(first);
- if let Some(second) = second {
- segments.push(second);
- }
- }
- segments
- };
- let meta = match iter.peek() {
- Some(NodeOrToken::Token(eq)) if eq.kind() == T![=] => {
- iter.next();
- let value = match iter.peek() {
- Some(NodeOrToken::Token(token)) if token.kind().is_literal() => {
- // No need to consume it, it will be consumed by `extract_and_eat_comma()`.
- Some(token.clone())
- }
- _ => None,
- };
- let name = if second_segment.is_none() { Some(first_segment) } else { None };
- Meta::NamedKeyValue { path_range, name, value }
- }
- Some(NodeOrToken::Node(tt)) => Meta::TokenTree {
- path: AttrPath {
- segments: segments(first_segment, second_segment),
- range: path_range,
- is_test,
- },
- tt: tt.clone(),
- },
- _ => Meta::Path {
- path: AttrPath {
- segments: segments(first_segment, second_segment),
- range: path_range,
- is_test,
- },
- },
- };
- Some((meta, start_offset))
+ fn as_up_to_two_segment(&self) -> Option<(SmolStr, Option<SmolStr>)> {
+ self.as_ref().and_then(|it| it.as_up_to_two_segment())
}
+}
+
+pub trait AstKeyValueMetaExt {
+ fn value_string(&self) -> Option<SmolStr>;
+}
- fn extract_possibly_unsafe(
- iter: &mut Peekable<TokenTreeChildren>,
- container: &ast::TokenTree,
- ) -> Option<(Self, TextRange)> {
- if iter.peek().is_some_and(|it| it.as_token().is_some_and(|it| it.kind() == T![unsafe])) {
- iter.next();
- let tt = iter.next()?.into_node()?;
- let result = Self::extract(&mut TokenTreeChildren::new(&tt).peekable()).map(
- |(meta, start_offset)| (meta, TextRange::new(start_offset, tt_end_offset(&tt))),
- );
- while iter.next().is_some_and(|it| it.as_token().is_none_or(|it| it.kind() != T![,])) {}
- result
+impl AstKeyValueMetaExt for ast::KeyValueMeta {
+ fn value_string(&self) -> Option<SmolStr> {
+ if let Some(ast::Expr::Literal(value)) = self.expr()
+ && let ast::LiteralKind::String(value) = value.kind()
+ && let Ok(value) = value.value()
+ {
+ Some((*value).into())
} else {
- Self::extract(iter).map(|(meta, start_offset)| {
- let end_offset = 'find_end_offset: {
- for it in iter {
- if let NodeOrToken::Token(it) = it
- && it.kind() == T![,]
- {
- break 'find_end_offset it.text_range().start();
- }
- }
- tt_end_offset(container)
- };
- (meta, TextRange::new(start_offset, end_offset))
- })
+ None
}
}
}
-fn tt_end_offset(tt: &ast::TokenTree) -> TextSize {
- tt.syntax().last_token().unwrap().text_range().start()
-}
-
-/// The callback is passed a desugared form of the attribute ([`Meta`]), a [`SyntaxNode`] fully containing it
-/// (note: it may not be the direct parent), the range within the [`SyntaxNode`] bounding the attribute,
-/// and the outermost `ast::Attr`. Note that one node may map to multiple [`Meta`]s due to `cfg_attr`.
+/// The callback is passed the attribute and the outermost `ast::Attr`.
+/// Note that one node may map to multiple [`Meta`]s due to `cfg_attr`.
+///
+/// `unsafe(attr)` are passed the inner attribute for now.
#[inline]
pub fn expand_cfg_attr<'a, BreakValue>(
attrs: impl Iterator<Item = ast::Attr>,
cfg_options: impl FnMut() -> &'a CfgOptions,
- mut callback: impl FnMut(Meta, &SyntaxNode, TextRange, &ast::Attr) -> ControlFlow<BreakValue>,
+ mut callback: impl FnMut(ast::Meta, ast::Attr) -> ControlFlow<BreakValue>,
) -> Option<BreakValue> {
expand_cfg_attr_with_doc_comments::<Infallible, _>(
attrs.map(Either::Left),
cfg_options,
- move |Either::Left((meta, container, range, top_attr))| {
- callback(meta, container, range, top_attr)
- },
+ move |Either::Left((meta, top_attr))| callback(meta, top_attr),
)
}
@@ -264,66 +112,47 @@ pub fn expand_cfg_attr<'a, BreakValue>(
pub fn expand_cfg_attr_with_doc_comments<'a, DocComment, BreakValue>(
mut attrs: impl Iterator<Item = Either<ast::Attr, DocComment>>,
mut cfg_options: impl FnMut() -> &'a CfgOptions,
- mut callback: impl FnMut(
- Either<(Meta, &SyntaxNode, TextRange, &ast::Attr), DocComment>,
- ) -> ControlFlow<BreakValue>,
+ mut callback: impl FnMut(Either<(ast::Meta, ast::Attr), DocComment>) -> ControlFlow<BreakValue>,
) -> Option<BreakValue> {
let mut stack = SmallVec::<[_; 1]>::new();
- let result = attrs.try_for_each(|top_attr| {
- let top_attr = match top_attr {
- Either::Left(it) => it,
- Either::Right(comment) => return callback(Either::Right(comment)),
- };
- if let Some((attr_name, tt)) = top_attr.as_simple_call()
- && attr_name == "cfg_attr"
- {
- let mut tt_iter = TokenTreeChildren::new(&tt).peekable();
- let cfg = cfg::CfgExpr::parse_from_ast(&mut tt_iter);
- if cfg_options().check(&cfg) != Some(false) {
- stack.push((tt_iter, tt));
- while let Some((tt_iter, tt)) = stack.last_mut() {
- let Some((attr, range)) = Meta::extract_possibly_unsafe(tt_iter, tt) else {
- stack.pop();
- continue;
- };
- if let Meta::TokenTree { path, tt: nested_tt } = &attr
- && path.is1("cfg_attr")
- {
- let mut nested_tt_iter = TokenTreeChildren::new(nested_tt).peekable();
- let cfg = cfg::CfgExpr::parse_from_ast(&mut nested_tt_iter);
- if cfg_options().check(&cfg) != Some(false) {
- stack.push((nested_tt_iter, nested_tt.clone()));
- }
- } else {
- callback(Either::Left((attr, tt.syntax(), range, &top_attr)))?;
+ loop {
+ let (mut meta, top_attr) = if let Some(it) = stack.pop() {
+ it
+ } else {
+ let attr = attrs.next()?;
+ match attr {
+ Either::Left(attr) => {
+ let Some(meta) = attr.meta() else { continue };
+ stack.push((meta, attr));
+ }
+ Either::Right(doc_comment) => {
+ if let ControlFlow::Break(break_value) = callback(Either::Right(doc_comment)) {
+ return Some(break_value);
}
}
}
- } else if let Some(ast_meta) = top_attr.meta()
- && let Some(path) = ast_meta.path()
- {
- let path = AttrPath::extract(&path);
- let meta = if let Some(tt) = ast_meta.token_tree() {
- Meta::TokenTree { path, tt }
- } else if let Some(value) = ast_meta.expr() {
- let value =
- if let ast::Expr::Literal(value) = value { Some(value.token()) } else { None };
- let name =
- if path.segments.len() == 1 { Some(path.segments[0].clone()) } else { None };
- Meta::NamedKeyValue { name, value, path_range: path.range }
- } else {
- Meta::Path { path }
- };
- callback(Either::Left((
- meta,
- ast_meta.syntax(),
- ast_meta.syntax().text_range(),
- &top_attr,
- )))?;
+ continue;
+ };
+
+ while let ast::Meta::UnsafeMeta(unsafe_meta) = &meta {
+ let Some(inner) = unsafe_meta.meta() else { continue };
+ meta = inner;
}
- ControlFlow::Continue(())
- });
- result.break_value()
+
+ if let ast::Meta::CfgAttrMeta(meta) = meta {
+ let Some(cfg_predicate) = meta.cfg_predicate() else { continue };
+ let cfg_predicate = CfgExpr::parse_from_ast(cfg_predicate);
+ if cfg_options().check(&cfg_predicate) != Some(false) {
+ let prev_stack_len = stack.len();
+ stack.extend(meta.metas().map(|meta| (meta, top_attr.clone())));
+ stack[prev_stack_len..].reverse();
+ }
+ } else {
+ if let ControlFlow::Break(break_value) = callback(Either::Left((meta, top_attr))) {
+ return Some(break_value);
+ }
+ }
+ }
}
#[inline]
@@ -351,39 +180,33 @@ pub(crate) fn is_item_tree_filtered_attr(name: &str) -> bool {
pub fn collect_item_tree_attrs<'a, BreakValue>(
owner: &dyn ast::HasAttrs,
cfg_options: impl Fn() -> &'a CfgOptions,
- mut on_attr: impl FnMut(Meta, &SyntaxNode, &ast::Attr, TextRange) -> ControlFlow<BreakValue>,
+ mut on_attr: impl FnMut(ast::Meta, ast::Attr) -> ControlFlow<BreakValue>,
) -> Option<Either<BreakValue, CfgExpr>> {
let attrs = ast::attrs_including_inner(owner);
expand_cfg_attr(
attrs,
|| cfg_options(),
- |attr, container, range, top_attr| {
+ |attr, top_attr| {
// We filter builtin attributes that we don't need for nameres, because this saves memory.
// I only put the most common attributes, but if some attribute becomes common feel free to add it.
// Notice, however: for an attribute to be filtered out, it *must* not be shadowable with a macro!
let filter = match &attr {
- Meta::NamedKeyValue { name: Some(name), .. } => {
- is_item_tree_filtered_attr(name.text())
- }
- Meta::TokenTree { path, tt } if path.segments.len() == 1 => {
- let name = path.segments[0].text();
- if name == "cfg" {
- let cfg =
- CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(tt).peekable());
- if cfg_options().check(&cfg) == Some(false) {
- return ControlFlow::Break(Either::Right(cfg));
- }
- true
- } else {
- is_item_tree_filtered_attr(name)
+ ast::Meta::CfgMeta(attr) => {
+ let Some(cfg_predicate) = attr.cfg_predicate() else {
+ return ControlFlow::Continue(());
+ };
+ let cfg = CfgExpr::parse_from_ast(cfg_predicate);
+ if cfg_options().check(&cfg) == Some(false) {
+ return ControlFlow::Break(Either::Right(cfg));
}
+ true
}
- Meta::Path { path } => {
- path.segments.len() == 1 && is_item_tree_filtered_attr(path.segments[0].text())
- }
- _ => false,
+ _ => attr
+ .path()
+ .and_then(|path| path.as_one_segment())
+ .is_some_and(|segment| is_item_tree_filtered_attr(&segment)),
};
- if !filter && let ControlFlow::Break(v) = on_attr(attr, container, top_attr, range) {
+ if !filter && let ControlFlow::Break(v) = on_attr(attr, top_attr) {
return ControlFlow::Break(Either::Left(v));
}
ControlFlow::Continue(())
@@ -540,34 +363,32 @@ impl AttrId {
}
/// Returns the containing `ast::Attr` (note that it may contain other attributes as well due
- /// to `cfg_attr`), a `SyntaxNode` guaranteed to contain the attribute, the full range of the
- /// attribute, and its desugared [`Meta`].
+ /// to `cfg_attr`) and its [`ast::Meta`].
pub fn find_attr_range<N: ast::HasAttrs>(
self,
db: &dyn ExpandDatabase,
krate: Crate,
owner: AstId<N>,
- ) -> (ast::Attr, SyntaxNode, TextRange, Meta) {
+ ) -> (ast::Attr, ast::Meta) {
self.find_attr_range_with_source(db, krate, &owner.to_node(db))
}
/// Returns the containing `ast::Attr` (note that it may contain other attributes as well due
- /// to `cfg_attr`), a `SyntaxNode` guaranteed to contain the attribute, the full range of the
- /// attribute, and its desugared [`Meta`].
+ /// to `cfg_attr`) and its [`ast::Meta`].
pub fn find_attr_range_with_source(
self,
db: &dyn ExpandDatabase,
krate: Crate,
owner: &dyn ast::HasAttrs,
- ) -> (ast::Attr, SyntaxNode, TextRange, Meta) {
+ ) -> (ast::Attr, ast::Meta) {
let cfg_options = OnceCell::new();
let mut index = 0;
let result = collect_item_tree_attrs(
owner,
|| cfg_options.get_or_init(|| krate.cfg_options(db)),
- |meta, container, top_attr, range| {
+ |meta, top_attr| {
if index == self.id {
- return ControlFlow::Break((top_attr.clone(), container.clone(), range, meta));
+ return ControlFlow::Break((top_attr, meta));
}
index += 1;
ControlFlow::Continue(())
@@ -588,9 +409,12 @@ impl AttrId {
owner: AstId<ast::Adt>,
derive_index: u32,
) -> TextRange {
- let (_, _, derive_attr_range, derive_attr) = self.find_attr_range(db, krate, owner);
- let Meta::TokenTree { tt, .. } = derive_attr else {
- return derive_attr_range;
+ let (_, derive_attr) = self.find_attr_range(db, krate, owner);
+ let ast::Meta::TokenTreeMeta(derive_attr) = derive_attr else {
+ return derive_attr.syntax().text_range();
+ };
+ let Some(tt) = derive_attr.token_tree() else {
+ return derive_attr.syntax().text_range();
};
// Fake the span map, as we don't really need spans here, just the offsets of the node in the file.
let span_map = RealSpanMap::absolute(span::EditionedFileId::current_edition(
@@ -605,11 +429,11 @@ impl AttrId {
let Some((_, _, derive_tts)) =
parse_path_comma_token_tree(db, &tt).nth(derive_index as usize)
else {
- return derive_attr_range;
+ return derive_attr.syntax().text_range();
};
let (Some(first_span), Some(last_span)) = (derive_tts.first_span(), derive_tts.last_span())
else {
- return derive_attr_range;
+ return derive_attr.syntax().text_range();
};
let start = first_span.range.start();
let end = last_span.range.end();
diff --git a/crates/hir-expand/src/cfg_process.rs b/crates/hir-expand/src/cfg_process.rs
index ccef9168ac..6258fac0e9 100644
--- a/crates/hir-expand/src/cfg_process.rs
+++ b/crates/hir-expand/src/cfg_process.rs
@@ -8,12 +8,12 @@ use parser::T;
use smallvec::SmallVec;
use syntax::{
AstNode, PreorderWithTokens, SyntaxElement, SyntaxNode, SyntaxToken, WalkEvent,
- ast::{self, HasAttrs, TokenTreeChildren},
+ ast::{self, HasAttrs},
};
use syntax_bridge::DocCommentDesugarMode;
use crate::{
- attrs::{AttrId, Meta, expand_cfg_attr, is_item_tree_filtered_attr},
+ attrs::{AstPathExt, AttrId, expand_cfg_attr, is_item_tree_filtered_attr},
db::ExpandDatabase,
fixup::{self, SyntaxFixupUndoInfo},
span_map::SpanMapRef,
@@ -24,7 +24,7 @@ struct ItemIsCfgedOut;
#[derive(Debug)]
struct ExpandedAttrToProcess {
- range: TextRange,
+ attr: ast::Meta,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -143,42 +143,29 @@ fn macro_input_callback(
});
attrs_idx = 0;
- let strip_current_item = expand_cfg_attr(
- node_attrs,
- &cfg_options,
- |attr, _container, range, top_attr| {
+ let strip_current_item =
+ expand_cfg_attr(node_attrs, &cfg_options, |attr, top_attr| {
// Find the attr.
while attrs[attrs_idx].range != top_attr.syntax().text_range() {
attrs_idx += 1;
}
let mut strip_current_attr = false;
- match attr {
- Meta::NamedKeyValue { name, .. } => {
- if name
- .is_none_or(|name| !is_item_tree_filtered_attr(name.text()))
- {
- strip_current_attr = should_strip_attr();
- }
- }
- Meta::TokenTree { path, tt } => {
- if path.is1("cfg") {
- let cfg_expr = CfgExpr::parse_from_ast(
- &mut TokenTreeChildren::new(&tt).peekable(),
- );
+ match &attr {
+ ast::Meta::CfgMeta(attr) => {
+ if let Some(cfg_predicate) = attr.cfg_predicate() {
+ let cfg_expr = CfgExpr::parse_from_ast(cfg_predicate);
if cfg_options().check(&cfg_expr) == Some(false) {
return ControlFlow::Break(ItemIsCfgedOut);
}
strip_current_attr = true;
- } else if path.segments.len() != 1
- || !is_item_tree_filtered_attr(path.segments[0].text())
- {
- strip_current_attr = should_strip_attr();
}
}
- Meta::Path { path } => {
- if path.segments.len() != 1
- || !is_item_tree_filtered_attr(path.segments[0].text())
+ _ => {
+ if attr
+ .path()
+ .as_one_segment()
+ .is_none_or(|name| !is_item_tree_filtered_attr(&name))
{
strip_current_attr = should_strip_attr();
}
@@ -188,12 +175,11 @@ fn macro_input_callback(
if !strip_current_attr {
attrs[attrs_idx]
.expanded_attrs
- .push(ExpandedAttrToProcess { range });
+ .push(ExpandedAttrToProcess { attr });
}
ControlFlow::Continue(())
- },
- );
+ });
attrs_idx = 0;
if strip_current_item.is_some() {
@@ -248,7 +234,7 @@ fn macro_input_callback(
};
match ast_attr.next_expanded_attr {
NextExpandedAttrState::NotStarted => {
- if token_range.start() >= expanded_attr.range.start() {
+ if token_range.start() >= expanded_attr.attr.syntax().text_range().start() {
// We started the next attribute.
let mut insert_tokens = Vec::with_capacity(3);
insert_tokens.push(tt::Leaf::Punct(tt::Punct {
@@ -278,7 +264,7 @@ fn macro_input_callback(
}
}
NextExpandedAttrState::InTheMiddle => {
- if token_range.start() >= expanded_attr.range.end() {
+ if token_range.start() >= expanded_attr.attr.syntax().text_range().end() {
// Finished the current attribute.
let insert_tokens = vec![tt::Leaf::Punct(tt::Punct {
char: ']',
@@ -329,12 +315,3 @@ pub(crate) fn attr_macro_input_to_token_tree(
fixups.undo_info,
)
}
-
-pub fn check_cfg_attr_value(
- db: &dyn ExpandDatabase,
- attr: &ast::TokenTree,
- krate: Crate,
-) -> Option<bool> {
- let cfg_expr = CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(attr).peekable());
- krate.cfg_options(db).check(&cfg_expr)
-}
diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs
index 8a6b56d932..8dddddfabb 100644
--- a/crates/hir-expand/src/db.rs
+++ b/crates/hir-expand/src/db.rs
@@ -11,7 +11,6 @@ use crate::{
AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo,
EagerExpander, EditionedFileId, ExpandError, ExpandResult, ExpandTo, FileRange, HirFileId,
MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
- attrs::Meta,
builtin::pseudo_derive_attr_expansion,
cfg_process::attr_macro_input_to_token_tree,
declarative::DeclarativeMacroExpander,
@@ -239,8 +238,15 @@ pub fn expand_speculative(
MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => {
if loc.def.is_attribute_derive() {
// for pseudo-derive expansion we actually pass the attribute itself only
- ast::Attr::cast(speculative_args.clone()).and_then(|attr| attr.token_tree()).map(
- |token_tree| {
+ ast::Attr::cast(speculative_args.clone())
+ .and_then(|attr| {
+ if let ast::Meta::TokenTreeMeta(meta) = attr.meta()? {
+ meta.token_tree()
+ } else {
+ None
+ }
+ })
+ .map(|token_tree| {
let mut tree = syntax_node_to_token_tree(
token_tree.syntax(),
span_map,
@@ -250,26 +256,26 @@ pub fn expand_speculative(
tree.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
tree.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(span));
tree
- },
- )
+ })
} else {
// Attributes may have an input token tree, build the subtree and map for this as well
// then try finding a token id for our token if it is inside this input subtree.
let item = ast::Item::cast(speculative_args.clone())?;
- let (_, _, _, meta) =
+ let (_, meta) =
attr_ids.invoc_attr().find_attr_range_with_source(db, loc.krate, &item);
- match meta {
- Meta::TokenTree { tt, .. } => {
- let mut attr_arg = syntax_bridge::syntax_node_to_token_tree(
- tt.syntax(),
- span_map,
- span,
- DocCommentDesugarMode::ProcMacro,
- );
- attr_arg.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
- Some(attr_arg)
- }
- _ => None,
+ if let ast::Meta::TokenTreeMeta(meta) = meta
+ && let Some(tt) = meta.token_tree()
+ {
+ let mut attr_arg = syntax_bridge::syntax_node_to_token_tree(
+ tt.syntax(),
+ span_map,
+ span,
+ DocCommentDesugarMode::ProcMacro,
+ );
+ attr_arg.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
+ Some(attr_arg)
+ } else {
+ None
}
}
}
@@ -501,11 +507,11 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
}
MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
let node = ast_id.to_ptr(db).to_node(&root);
- let range = attr_ids
- .invoc_attr()
- .find_attr_range_with_source(db, loc.krate, &node)
- .3
- .path_range();
+ let (_, attr) = attr_ids.invoc_attr().find_attr_range_with_source(db, loc.krate, &node);
+ let range = attr
+ .path()
+ .map(|path| path.syntax().text_range())
+ .unwrap_or_else(|| attr.syntax().text_range());
let span = map.span_for_range(range);
let is_derive = matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive());
diff --git a/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs
index 1726412275..4b2c6e7351 100644
--- a/crates/hir-expand/src/declarative.rs
+++ b/crates/hir-expand/src/declarative.rs
@@ -6,7 +6,7 @@ use base_db::Crate;
use span::{Edition, Span, SyntaxContext};
use stdx::TupleExt;
use syntax::{
- AstNode, AstToken,
+ AstNode,
ast::{self, HasAttrs},
};
use syntax_bridge::DocCommentDesugarMode;
@@ -15,7 +15,7 @@ use triomphe::Arc;
use crate::{
AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, Lookup, MacroCallId,
MacroCallStyle,
- attrs::{Meta, expand_cfg_attr},
+ attrs::{AstKeyValueMetaExt, AstPathExt, expand_cfg_attr},
db::ExpandDatabase,
hygiene::{Transparency, apply_mark},
tt,
@@ -92,11 +92,10 @@ impl DeclarativeMacroExpander {
expand_cfg_attr(
node.attrs(),
|| cfg_options.get_or_init(|| def_crate.cfg_options(db)),
- |attr, _, _, _| {
- if let Meta::NamedKeyValue { name: Some(name), value, .. } = attr
- && name.text() == "rustc_macro_transparency"
- && let Some(value) = value.and_then(ast::String::cast)
- && let Ok(value) = value.value()
+ |attr, _| {
+ if let ast::Meta::KeyValueMeta(attr) = attr
+ && attr.path().is1("rustc_macro_transparency")
+ && let Some(value) = attr.value_string()
{
match &*value {
"transparent" => ControlFlow::Break(Transparency::Transparent),
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index 4b2c75ed38..8d42a24e2f 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -58,7 +58,6 @@ use crate::{
};
pub use crate::{
- cfg_process::check_cfg_attr_value,
files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRealFile},
prettify_macro_expansion_::prettify_macro_expansion,
};
@@ -635,14 +634,12 @@ impl MacroCallLoc {
ast_id.with_value(ast_id.to_node(db).syntax().clone())
}
MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
- // FIXME: handle `cfg_attr`
- let (attr, _, _, _) = derive_attr_index.find_attr_range(db, self.krate, *ast_id);
+ let (_, attr) = derive_attr_index.find_attr_range(db, self.krate, *ast_id);
ast_id.with_value(attr.syntax().clone())
}
MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
if self.def.is_attribute_derive() {
- let (attr, _, _, _) =
- attr_ids.invoc_attr().find_attr_range(db, self.krate, *ast_id);
+ let (_, attr) = attr_ids.invoc_attr().find_attr_range(db, self.krate, *ast_id);
ast_id.with_value(attr.syntax().clone())
} else {
ast_id.with_value(ast_id.to_node(db).syntax().clone())
@@ -770,11 +767,11 @@ impl MacroCallKind {
}
MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
// FIXME: should be the range of the macro name, not the whole derive
- derive_attr_index.find_attr_range(db, krate, ast_id).2
+ derive_attr_index.find_attr_range(db, krate, ast_id).1.syntax().text_range()
}
// FIXME: handle `cfg_attr`
MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
- attr_ids.invoc_attr().find_attr_range(db, krate, ast_id).2
+ attr_ids.invoc_attr().find_attr_range(db, krate, ast_id).1.syntax().text_range()
}
};
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 89f3cfd140..2829902035 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1239,11 +1239,15 @@ fn emit_def_diagnostic_<'db>(
);
}
DefDiagnosticKind::InvalidDeriveTarget { ast, id } => {
- let derive = id.find_attr_range(db, krate, *ast).3.path_range();
+ let (_, attr) = id.find_attr_range(db, krate, *ast);
+ let derive = attr
+ .path()
+ .map(|path| path.syntax().text_range())
+ .unwrap_or_else(|| attr.syntax().text_range());
acc.push(InvalidDeriveTarget { range: ast.with_value(derive) }.into());
}
DefDiagnosticKind::MalformedDerive { ast, id } => {
- let derive = id.find_attr_range(db, krate, *ast).2;
+ let derive = id.find_attr_range(db, krate, *ast).1.syntax().text_range();
acc.push(MalformedDerive { range: ast.with_value(derive) }.into());
}
DefDiagnosticKind::MacroDefError { ast, message } => {
@@ -1283,7 +1287,8 @@ fn precise_macro_call_location(
ast_id.with_value(range)
}
MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
- let attr_range = attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).2;
+ let attr_range =
+ attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).1.syntax().text_range();
ast_id.with_value(attr_range)
}
}
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 9a31a08ffb..9996162485 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -24,6 +24,7 @@ use hir_def::{
};
use hir_expand::{
EditionedFileId, ExpandResult, FileRange, HirFileId, InMacroFile, MacroCallId,
+ attrs::AstPathExt,
builtin::{BuiltinFnLikeExpander, EagerExpander},
db::ExpandDatabase,
files::{FileRangeWrapper, HirFileRange, InRealFile},
@@ -298,14 +299,15 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> {
hir_expand::attrs::expand_cfg_attr::<Infallible>(
extra_crate_attrs.chain(ast::attrs_including_inner(&item)),
cfg_options,
- |attr, _, _, _| {
- let hir_expand::attrs::Meta::TokenTree { path, tt } = attr else {
+ |attr, _| {
+ let ast::Meta::TokenTreeMeta(attr) = attr else {
return ControlFlow::Continue(());
};
- if path.segments.len() != 1 {
+ let (Some(segment), Some(tt)) = (attr.path().as_one_segment(), attr.token_tree())
+ else {
return ControlFlow::Continue(());
- }
- let lint_attr = match path.segments[0].text() {
+ };
+ let lint_attr = match &*segment {
"allow" => LintAttr::Allow,
"expect" => LintAttr::Expect,
"warn" => LintAttr::Warn,
@@ -554,17 +556,6 @@ impl<'db> SemanticsImpl<'db> {
Some(InFile::new(file_id.into(), node))
}
- pub fn check_cfg_attr(&self, attr: &ast::TokenTree) -> Option<bool> {
- let file_id = self.find_file(attr.syntax()).file_id;
- let krate = match file_id {
- HirFileId::FileId(file_id) => {
- self.file_to_module_defs(file_id.file_id(self.db)).next()?.krate(self.db).id
- }
- HirFileId::MacroFile(macro_file) => self.db.lookup_intern_macro_call(macro_file).krate,
- };
- hir_expand::check_cfg_attr_value(self.db, attr, krate)
- }
-
/// Expands the macro if it isn't one of the built-in ones that expand to custom syntax or dummy
/// expansions.
pub fn expand_allowed_builtins(
@@ -608,8 +599,8 @@ impl<'db> SemanticsImpl<'db> {
Some(self.expand(macro_call_id).map(|it| InFile::new(macro_call_id.into(), it)))
}
- pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
- let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
+ pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Meta) -> Option<SyntaxNode> {
+ let adt = attr.parent_attr()?.syntax().parent().and_then(ast::Adt::cast)?;
let src = self.wrap_node_infile(attr.clone());
let call_id = self.with_ctx(|ctx| {
ctx.attr_to_derive_macro_call(src.with_value(&adt), src).map(|(_, it, _)| it)
@@ -617,7 +608,7 @@ impl<'db> SemanticsImpl<'db> {
Some(self.parse_or_expand(call_id.into()))
}
- pub fn resolve_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<Option<Macro>>> {
+ pub fn resolve_derive_macro(&self, attr: &ast::Meta) -> Option<Vec<Option<Macro>>> {
let calls = self.derive_macro_calls(attr)?;
self.with_ctx(|ctx| {
Some(
@@ -644,7 +635,7 @@ impl<'db> SemanticsImpl<'db> {
pub fn expand_derive_macro(
&self,
- attr: &ast::Attr,
+ attr: &ast::Meta,
) -> Option<Vec<Option<ExpandResult<SyntaxNode>>>> {
let res: Vec<_> = self
.derive_macro_calls(attr)?
@@ -662,9 +653,9 @@ impl<'db> SemanticsImpl<'db> {
fn derive_macro_calls(
&self,
- attr: &ast::Attr,
+ attr: &ast::Meta,
) -> Option<Vec<Option<Either<MacroCallId, BuiltinDeriveImplId>>>> {
- let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
+ let adt = attr.parent_attr()?.syntax().parent().and_then(ast::Adt::cast)?;
let file_id = self.find_file(adt.syntax()).file_id;
let adt = InFile::new(file_id, &adt);
let src = InFile::new(file_id, attr.clone());
@@ -773,7 +764,11 @@ impl<'db> SemanticsImpl<'db> {
let attr = self.wrap_node_infile(actual_macro_call.clone());
let adt = actual_macro_call.syntax().parent().and_then(ast::Adt::cast)?;
let macro_call_id = self.with_ctx(|ctx| {
- ctx.attr_to_derive_macro_call(attr.with_value(&adt), attr).map(|(_, it, _)| it)
+ ctx.attr_to_derive_macro_call(
+ attr.with_value(&adt),
+ attr.with_value(attr.value.meta()?),
+ )
+ .map(|(_, it, _)| it)
})?;
hir_expand::db::expand_speculative(
self.db,
@@ -1328,7 +1323,7 @@ impl<'db> SemanticsImpl<'db> {
// text ranges of the outer ones, and then all of the inner ones up
// to the invoking attribute so that the inbetween is ignored.
// FIXME: Should cfg_attr be handled differently?
- let (attr, _, _, _) = attr_ids
+ let (attr, _) = attr_ids
.invoc_attr()
.find_attr_range_with_source(db, loc.krate, &item);
let start = attr.syntax().text_range().start();
@@ -1435,7 +1430,7 @@ impl<'db> SemanticsImpl<'db> {
let derive_call = ctx
.attr_to_derive_macro_call(
InFile::new(expansion, &adt),
- InFile::new(expansion, attr.clone()),
+ InFile::new(expansion, meta.clone()),
)?
.1;
diff --git a/crates/hir/src/semantics/child_by_source.rs b/crates/hir/src/semantics/child_by_source.rs
index f6d1bec575..babeb35913 100644
--- a/crates/hir/src/semantics/child_by_source.rs
+++ b/crates/hir/src/semantics/child_by_source.rs
@@ -126,8 +126,7 @@ impl ChildBySource for ItemScope {
calls.for_each(|(attr_id, call_id, calls)| {
// FIXME: Is this the right crate?
let krate = call_id.lookup(db).krate;
- // FIXME: Fix cfg_attr handling.
- let (attr, _, _, _) = attr_id.find_attr_range_with_source(db, krate, &adt);
+ let (_, attr) = attr_id.find_attr_range_with_source(db, krate, &adt);
res[keys::DERIVE_MACRO_CALL]
.insert(AstPtr::new(&attr), (attr_id, call_id, calls.into()));
});
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 59bccc22d8..d932198b43 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -398,7 +398,7 @@ impl SourceToDefCtx<'_, '_> {
pub(super) fn attr_to_derive_macro_call(
&mut self,
item: InFile<&ast::Adt>,
- src: InFile<ast::Attr>,
+ src: InFile<ast::Meta>,
) -> Option<(AttrId, MacroCallId, &[Option<Either<MacroCallId, BuiltinDeriveImplId>>])> {
let map = self.dyn_map(item)?;
map[keys::DERIVE_MACRO_CALL]
@@ -423,6 +423,7 @@ impl SourceToDefCtx<'_, '_> {
let dyn_map = &map[keys::DERIVE_MACRO_CALL];
adt.value
.attrs()
+ .flat_map(|attr| attr.skip_cfg_attrs())
.filter_map(move |attr| dyn_map.get(&AstPtr::new(&attr)))
.map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids))
})
diff --git a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs
index e022a27e51..fccc04770e 100644
--- a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs
@@ -279,7 +279,7 @@ fn todo_fn(f: &ast::Fn, config: &AssistConfig) -> ast::Fn {
}
fn cfg_attrs(node: &impl HasAttrs) -> impl Iterator<Item = ast::Attr> {
- node.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"))
+ node.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))))
}
#[cfg(test)]
diff --git a/crates/ide-assists/src/handlers/generate_derive.rs b/crates/ide-assists/src/handlers/generate_derive.rs
index 3ef68f06e4..7aeb5e3396 100644
--- a/crates/ide-assists/src/handlers/generate_derive.rs
+++ b/crates/ide-assists/src/handlers/generate_derive.rs
@@ -68,9 +68,11 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
],
);
- let delimiter = derive
- .meta()
- .expect("make::attr_outer was expected to have Meta")
+ let meta = derive.meta().expect("make::attr_outer was expected to have Meta");
+ let ast::Meta::TokenTreeMeta(meta) = meta else {
+ unreachable!("make::attr_outer was passed a token tree meta");
+ };
+ let delimiter = meta
.token_tree()
.expect("failed to get token tree out of Meta")
.r_paren_token()
diff --git a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
index 2fc2b9efe8..7746cdc068 100644
--- a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
+++ b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
@@ -121,9 +121,8 @@ pub(crate) fn generate_single_field_struct_from(
)
.indent_with_mapping(1.into(), &make);
- let cfg_attrs = strukt
- .attrs()
- .filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"));
+ let cfg_attrs =
+ strukt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))));
let impl_ = make.impl_trait(
cfg_attrs,
diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
index 04c9d8e54d..5e595218f6 100644
--- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -64,9 +64,10 @@ pub(crate) fn replace_derive_with_manual_impl(
.filter_map(|attr| attr.path())
.collect::<Vec<_>>();
- let adt = value.parent().and_then(ast::Adt::cast)?;
- let attr = ast::Attr::cast(value)?;
- let args = attr.token_tree()?;
+ let attr = ast::Meta::cast(value)?.parent_attr()?;
+ let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
+ let ast::Meta::TokenTreeMeta(meta) = attr.meta()? else { return None };
+ let args = meta.token_tree()?;
let current_module = ctx.sema.scope(adt.syntax())?.module();
let current_crate = current_module.krate(ctx.db());
diff --git a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
index 36df4af31d..3b8988db7a 100644
--- a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
+++ b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
@@ -19,7 +19,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
// ->
// ```
-// #[cfg_attr($0, derive(Debug))]
+// #[cfg_attr(${0:cfg}, derive(Debug))]
// struct S {
// field: i32
// }
@@ -147,12 +147,15 @@ pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -
}
}?;
match option {
- WrapUnwrapOption::WrapAttr(attrs) => match &attrs[..] {
- [attr] if attr.simple_name().as_deref() == Some("cfg_attr") => {
- unwrap_cfg_attr(acc, attrs.into_iter().next().unwrap())
+ WrapUnwrapOption::WrapAttr(attrs) => {
+ if let [attr] = &attrs[..]
+ && let Some(ast::Meta::CfgAttrMeta(meta)) = attr.meta()
+ {
+ unwrap_cfg_attr(acc, meta)
+ } else {
+ wrap_cfg_attrs(acc, ctx, attrs)
}
- _ => wrap_cfg_attrs(acc, ctx, attrs),
- },
+ }
WrapUnwrapOption::WrapDerive { derive, attr } => wrap_derive(acc, ctx, attr, derive),
}
}
@@ -164,7 +167,8 @@ fn wrap_derive(
derive_element: TextRange,
) -> Option<()> {
let range = attr.syntax().text_range();
- let token_tree = attr.token_tree()?;
+ let ast::Meta::TokenTreeMeta(meta) = attr.meta()? else { return None };
+ let token_tree = meta.token_tree()?;
let mut path_text = String::new();
let mut cfg_derive_tokens = Vec::new();
@@ -193,20 +197,15 @@ fn wrap_derive(
let new_derive = make.attr_outer(
make.meta_token_tree(make.ident_path("derive"), make.token_tree(T!['('], new_derive)),
);
- let meta = make.meta_token_tree(
- make.ident_path("cfg_attr"),
- make.token_tree(
- T!['('],
- vec![
- NodeOrToken::Token(make.token(T![,])),
- NodeOrToken::Token(make.whitespace(" ")),
- NodeOrToken::Token(make.ident("derive")),
- NodeOrToken::Node(make.token_tree(T!['('], cfg_derive_tokens)),
- ],
- ),
+ let meta = make.cfg_attr_meta(
+ make.cfg_flag("cfg"),
+ [make.meta_token_tree(
+ make.ident_path("derive"),
+ make.token_tree(T!['('], cfg_derive_tokens),
+ )],
);
- let cfg_attr = make.attr_outer(meta);
+ let cfg_attr = make.attr_outer(meta.clone().into());
editor.replace_with_many(
attr.syntax(),
vec![
@@ -217,11 +216,10 @@ fn wrap_derive(
);
if let Some(snippet_cap) = ctx.config.snippet_cap
- && let Some(first_meta) =
- cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token())
+ && let Some(cfg_predicate) = meta.cfg_predicate()
{
- let tabstop = edit.make_tabstop_after(snippet_cap);
- editor.add_annotation(first_meta, tabstop);
+ let tabstop = edit.make_placeholder_snippet(snippet_cap);
+ editor.add_annotation(cfg_predicate.syntax(), tabstop);
}
editor.add_mappings(make.finish_with_mappings());
@@ -236,58 +234,29 @@ fn wrap_derive(
);
Some(())
}
+
fn wrap_cfg_attrs(acc: &mut Assists, ctx: &AssistContext<'_>, attrs: Vec<ast::Attr>) -> Option<()> {
let (first_attr, last_attr) = (attrs.first()?, attrs.last()?);
let range = first_attr.syntax().text_range().cover(last_attr.syntax().text_range());
- let path_attrs =
- attrs.iter().map(|attr| Some((attr.path()?, attr.clone()))).collect::<Option<Vec<_>>>()?;
let handle_source_change = |edit: &mut SourceChangeBuilder| {
let make = SyntaxFactory::with_mappings();
let mut editor = edit.make_editor(first_attr.syntax());
- let mut raw_tokens = vec![];
- for (path, attr) in path_attrs {
- raw_tokens.extend([
- NodeOrToken::Token(make.token(T![,])),
- NodeOrToken::Token(make.whitespace(" ")),
- ]);
- path.syntax().descendants_with_tokens().for_each(|it| {
- if let NodeOrToken::Token(token) = it {
- raw_tokens.push(NodeOrToken::Token(token));
- }
- });
- if let Some(meta) = attr.meta() {
- if let (Some(eq), Some(expr)) = (meta.eq_token(), meta.expr()) {
- raw_tokens.push(NodeOrToken::Token(make.whitespace(" ")));
- raw_tokens.push(NodeOrToken::Token(eq));
- raw_tokens.push(NodeOrToken::Token(make.whitespace(" ")));
-
- expr.syntax().descendants_with_tokens().for_each(|it| {
- if let NodeOrToken::Token(token) = it {
- raw_tokens.push(NodeOrToken::Token(token));
- }
- });
- } else if let Some(tt) = meta.token_tree() {
- raw_tokens.extend(tt.token_trees_and_tokens());
- }
- }
- }
let meta =
- make.meta_token_tree(make.ident_path("cfg_attr"), make.token_tree(T!['('], raw_tokens));
+ make.cfg_attr_meta(make.cfg_flag("cfg"), attrs.iter().filter_map(|attr| attr.meta()));
let cfg_attr = if first_attr.excl_token().is_some() {
- make.attr_inner(meta)
+ make.attr_inner(meta.clone().into())
} else {
- make.attr_outer(meta)
+ make.attr_outer(meta.clone().into())
};
let syntax_range = first_attr.syntax().clone().into()..=last_attr.syntax().clone().into();
editor.replace_all(syntax_range, vec![cfg_attr.syntax().clone().into()]);
if let Some(snippet_cap) = ctx.config.snippet_cap
- && let Some(first_meta) =
- cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token())
+ && let Some(cfg_flag) = meta.cfg_predicate()
{
- let tabstop = edit.make_tabstop_after(snippet_cap);
- editor.add_annotation(first_meta, tabstop);
+ let tabstop = edit.make_placeholder_snippet(snippet_cap);
+ editor.add_annotation(cfg_flag.syntax(), tabstop);
}
editor.add_mappings(make.finish_with_mappings());
@@ -301,66 +270,28 @@ fn wrap_cfg_attrs(acc: &mut Assists, ctx: &AssistContext<'_>, attrs: Vec<ast::At
);
Some(())
}
-fn unwrap_cfg_attr(acc: &mut Assists, attr: ast::Attr) -> Option<()> {
- let range = attr.syntax().text_range();
- let meta = attr.meta()?;
- let meta_tt = meta.token_tree()?;
- let mut inner_attrs = Vec::with_capacity(1);
- let mut found_comma = false;
- let mut iter = meta_tt.token_trees_and_tokens().skip(1).peekable();
- while let Some(tt) = iter.next() {
- if let NodeOrToken::Token(token) = &tt {
- if token.kind() == T![')'] {
- break;
- }
- if token.kind() == T![,] {
- found_comma = true;
- continue;
- }
- }
- if !found_comma {
- continue;
- }
- let Some(attr_name) = tt.into_token().and_then(|token| {
- if token.kind() == T![ident] { Some(make::ext::ident_path(token.text())) } else { None }
- }) else {
- continue;
- };
- let next_tt = iter.next()?;
- let meta = match next_tt {
- NodeOrToken::Node(tt) => make::meta_token_tree(attr_name, tt),
- NodeOrToken::Token(token) if token.kind() == T![,] || token.kind() == T![')'] => {
- make::meta_path(attr_name)
- }
- NodeOrToken::Token(token) => {
- let equals = algo::skip_trivia_token(token, syntax::Direction::Next)?;
- if equals.kind() != T![=] {
- return None;
- }
- let expr_token =
- algo::skip_trivia_token(equals.next_token()?, syntax::Direction::Next)
- .and_then(|it| {
- if it.kind().is_literal() {
- Some(make::expr_literal(it.text()))
- } else {
- None
- }
- })?;
- make::meta_expr(attr_name, ast::Expr::Literal(expr_token))
+
+fn unwrap_cfg_attr(acc: &mut Assists, meta: ast::CfgAttrMeta) -> Option<()> {
+ let top_attr = ast::Meta::from(meta.clone()).parent_attr()?;
+ let range = top_attr.syntax().text_range();
+ let inner_attrs = meta
+ .metas()
+ .map(|meta| {
+ if top_attr.excl_token().is_some() {
+ make::attr_inner(meta)
+ } else {
+ make::attr_outer(meta)
}
- };
- if attr.excl_token().is_some() {
- inner_attrs.push(make::attr_inner(meta));
- } else {
- inner_attrs.push(make::attr_outer(meta));
- }
- }
+ })
+ .collect::<Vec<_>>();
if inner_attrs.is_empty() {
return None;
}
let handle_source_change = |f: &mut SourceChangeBuilder| {
- let inner_attrs =
- inner_attrs.iter().map(|it| it.to_string()).join(&format!("\n{}", attr.indent_level()));
+ let inner_attrs = inner_attrs
+ .iter()
+ .map(|it| it.to_string())
+ .join(&format!("\n{}", top_attr.indent_level()));
f.replace(range, inner_attrs);
};
acc.add(
@@ -388,7 +319,7 @@ mod tests {
}
"#,
r#"
- #[cfg_attr($0, derive(Debug))]
+ #[cfg_attr(${0:cfg}, derive(Debug))]
pub struct Test {
test: u32,
}
@@ -422,7 +353,7 @@ mod tests {
"#,
r#"
pub struct Test {
- #[cfg_attr($0, foo)]
+ #[cfg_attr(${0:cfg}, foo)]
test: u32,
}
"#,
@@ -456,7 +387,7 @@ mod tests {
r#"
pub struct Test {
#[other_attr]
- #[cfg_attr($0, foo, bar)]
+ #[cfg_attr(${0:cfg}, foo, bar)]
#[other_attr]
test: u32,
}
@@ -491,7 +422,7 @@ mod tests {
"#,
r#"
pub struct Test {
- #[cfg_attr($0, foo = "bar")]
+ #[cfg_attr(${0:cfg}, foo = "bar")]
test: u32,
}
"#,
@@ -520,7 +451,7 @@ mod tests {
#![no_std$0]
"#,
r#"
- #![cfg_attr($0, no_std)]
+ #![cfg_attr(${0:cfg}, no_std)]
"#,
);
check_assist(
@@ -545,7 +476,7 @@ mod tests {
"#,
r#"
#[derive( Clone, Copy)]
- #[cfg_attr($0, derive(Debug))]
+ #[cfg_attr(${0:cfg}, derive(Debug))]
pub struct Test {
test: u32,
}
@@ -561,7 +492,7 @@ mod tests {
"#,
r#"
#[derive(Clone, Copy)]
- #[cfg_attr($0, derive(Debug))]
+ #[cfg_attr(${0:cfg}, derive(Debug))]
pub struct Test {
test: u32,
}
@@ -580,7 +511,7 @@ mod tests {
"#,
r#"
#[derive( Clone, Copy)]
- #[cfg_attr($0, derive(std::fmt::Debug))]
+ #[cfg_attr(${0:cfg}, derive(std::fmt::Debug))]
pub struct Test {
test: u32,
}
@@ -596,7 +527,7 @@ mod tests {
"#,
r#"
#[derive(Clone, Copy)]
- #[cfg_attr($0, derive(std::fmt::Debug))]
+ #[cfg_attr(${0:cfg}, derive(std::fmt::Debug))]
pub struct Test {
test: u32,
}
@@ -615,7 +546,7 @@ mod tests {
"#,
r#"
#[derive(std::fmt::Debug, Clone)]
- #[cfg_attr($0, derive(Copy))]
+ #[cfg_attr(${0:cfg}, derive(Copy))]
pub struct Test {
test: u32,
}
@@ -631,7 +562,7 @@ mod tests {
"#,
r#"
#[derive(Clone, Copy)]
- #[cfg_attr($0, derive(std::fmt::Debug))]
+ #[cfg_attr(${0:cfg}, derive(std::fmt::Debug))]
pub struct Test {
test: u32,
}
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 66d5cf834f..a499607c1f 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -3852,7 +3852,7 @@ struct S {
}
"#####,
r#####"
-#[cfg_attr($0, derive(Debug))]
+#[cfg_attr(${0:cfg}, derive(Debug))]
struct S {
field: i32
}
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index 3de8ec7f53..896743342c 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -598,9 +598,7 @@ fn generate_impl_text_inner(
// Copy any cfg attrs from the original adt
buf.push_str("\n\n");
- let cfg_attrs = adt
- .attrs()
- .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false));
+ let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))));
cfg_attrs.for_each(|attr| buf.push_str(&format!("{attr}\n")));
// `impl{generic_params} {trait_text} for {name}{generic_params.to_generic_args()}`
@@ -740,8 +738,7 @@ fn generate_impl_inner(
let ty = make::ty_path(make::ext::ident_path(&adt.name().unwrap().text()));
- let cfg_attrs =
- adt.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"));
+ let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))));
match trait_ {
Some(trait_) => make::impl_trait(
cfg_attrs,
@@ -811,8 +808,7 @@ fn generate_impl_inner_with_factory(
let ty: ast::Type = make.ty_path(make.ident_path(&adt.name().unwrap().text())).into();
- let cfg_attrs =
- adt.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"));
+ let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))));
match trait_ {
Some(trait_) => make.impl_trait(
cfg_attrs,
diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs
index 20776f6c49..da1e664f96 100644
--- a/crates/ide-completion/src/completions/attribute.rs
+++ b/crates/ide-completion/src/completions/attribute.rs
@@ -30,6 +30,7 @@ mod lint;
mod macro_use;
mod repr;
+pub(crate) use self::cfg::complete_cfg;
pub(crate) use self::derive::complete_derive_path;
/// Complete inputs to known builtin attributes as well as derive attributes
@@ -37,7 +38,7 @@ pub(crate) fn complete_known_attribute_input(
acc: &mut Completions,
ctx: &CompletionContext<'_>,
&colon_prefix: &bool,
- fake_attribute_under_caret: &ast::Attr,
+ fake_attribute_under_caret: &ast::TokenTreeMeta,
extern_crate: Option<&ast::ExternCrate>,
) -> Option<()> {
let attribute = fake_attribute_under_caret;
@@ -70,7 +71,6 @@ pub(crate) fn complete_known_attribute_input(
lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints);
}
- ["cfg"] | ["cfg_attr"] => cfg::complete_cfg(acc, ctx),
["macro_use"] => macro_use::complete_macro_use(
acc,
ctx,
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index ae3f717607..485e5f0caf 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -408,9 +408,11 @@ pub(crate) enum CompletionAnalysis<'db> {
/// Set if we are currently completing in an unexpanded attribute, this usually implies a builtin attribute like `allow($0)`
UnexpandedAttrTT {
colon_prefix: bool,
- fake_attribute_under_caret: Option<ast::Attr>,
+ fake_attribute_under_caret: Option<ast::TokenTreeMeta>,
extern_crate: Option<ast::ExternCrate>,
},
+ /// Set if we are inside the predicate of a #[cfg] or #[cfg_attr].
+ CfgPredicate,
MacroSegment,
}
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index d8f160c100..2a293313f2 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -284,9 +284,12 @@ fn expand(
};
// Expand pseudo-derive expansion aka `derive(Debug$0)`
- if let Some((orig_attr, spec_attr)) = attrs {
+ if let Some((orig_attr, spec_attr)) = attrs
+ && let Some(orig_meta) = orig_attr.meta()
+ {
+ // FIXME: Support speculative expansion with `cfg_attr`.
if let (Some(actual_expansion), Some((fake_expansion, fake_mapped_tokens))) = (
- sema.expand_derive_as_pseudo_attr_macro(&orig_attr),
+ sema.expand_derive_as_pseudo_attr_macro(&orig_meta),
sema.speculative_expand_derive_as_pseudo_attr_macro(
&orig_attr,
&spec_attr,
@@ -463,7 +466,9 @@ fn analyze<'db>(
}
// Overwrite the path kind for derives
- if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx {
+ if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx
+ && let Some(origin_meta) = origin_attr.meta()
+ {
if let Some(ast::NameLike::NameRef(name_ref)) =
find_node_at_offset(&file_with_fake_ident, offset)
{
@@ -473,7 +478,7 @@ fn analyze<'db>(
if let NameRefKind::Path(path_ctx) = &mut nameref_ctx.kind {
path_ctx.kind = PathKind::Derive {
existing_derives: sema
- .resolve_derive_macro(&origin_attr)
+ .resolve_derive_macro(&origin_meta)
.into_iter()
.flatten()
.flatten()
@@ -498,7 +503,7 @@ fn analyze<'db>(
let token = syntax::algo::skip_trivia_token(self_token.clone(), Direction::Prev)?;
let p = token.parent()?;
if p.kind() == SyntaxKind::TOKEN_TREE
- && p.ancestors().any(|it| it.kind() == SyntaxKind::META)
+ && p.ancestors().any(|it| it.kind() == SyntaxKind::TOKEN_TREE_META)
{
let colon_prefix = previous_non_trivia_token(self_token.clone())
.is_some_and(|it| T![:] == it.kind());
@@ -506,7 +511,7 @@ fn analyze<'db>(
CompletionAnalysis::UnexpandedAttrTT {
fake_attribute_under_caret: fake_ident_token
.parent_ancestors()
- .find_map(ast::Attr::cast),
+ .find_map(ast::TokenTreeMeta::cast),
colon_prefix,
extern_crate: p.ancestors().find_map(ast::ExternCrate::cast),
}
@@ -525,6 +530,13 @@ fn analyze<'db>(
} else {
return None;
}
+ } else if find_node_at_offset::<ast::CfgPredicate>(
+ &speculative_file,
+ speculative_offset,
+ )
+ .is_some()
+ {
+ CompletionAnalysis::CfgPredicate
} else {
return None;
}
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index 69ca2af772..3867e65ae5 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -263,6 +263,7 @@ pub fn completions(
extern_crate.as_ref(),
);
}
+ CompletionAnalysis::CfgPredicate => completions::attribute::complete_cfg(acc, ctx),
CompletionAnalysis::MacroSegment => {
completions::macro_def::complete_macro_segment(acc, ctx);
}
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs
index 2f696d07e2..9018552afb 100644
--- a/crates/ide-db/src/imports/import_assets.rs
+++ b/crates/ide-db/src/imports/import_assets.rs
@@ -117,7 +117,9 @@ impl PathDefinitionKinds {
// validate that the following segment resolve.
SyntaxKind::PATH => Self { modules: true, type_namespace: true, ..Self::ALL_DISABLED },
SyntaxKind::MACRO_CALL => Self { bang_macros: true, ..Self::ALL_DISABLED },
- SyntaxKind::META => Self { attr_macros: true, ..Self::ALL_DISABLED },
+ SyntaxKind::PATH_META | SyntaxKind::KEY_VALUE_META | SyntaxKind::TOKEN_TREE_META => {
+ Self { attr_macros: true, ..Self::ALL_DISABLED }
+ }
SyntaxKind::USE_TREE => {
if ast::UseTree::cast(parent).unwrap().use_tree_list().is_some() {
Self { modules: true, ..Self::ALL_DISABLED }
diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs
index 41ce1e5960..9318c3e132 100644
--- a/crates/ide-db/src/imports/insert_use.rs
+++ b/crates/ide-db/src/imports/insert_use.rs
@@ -101,14 +101,12 @@ impl ImportScope {
{
block = b.stmt_list();
}
- if has_attrs
- .attrs()
- .any(|attr| attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg"))
+ if has_attrs.attrs().any(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))))
{
if let Some(b) = block.clone() {
- let current_cfgs = has_attrs.attrs().filter(|attr| {
- attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg")
- });
+ let current_cfgs = has_attrs
+ .attrs()
+ .filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))));
let total_cfgs: Vec<_> =
required_cfgs.iter().cloned().chain(current_cfgs).collect();
@@ -118,7 +116,7 @@ impl ImportScope {
if let Some(parent) = parent {
can_merge = parent.children().filter_map(ast::Use::cast).any(|u| {
let u_attrs = u.attrs().filter(|attr| {
- attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg")
+ matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))
});
crate::imports::merge_imports::eq_attrs(
u_attrs,
@@ -134,9 +132,11 @@ impl ImportScope {
});
}
}
- required_cfgs.extend(has_attrs.attrs().filter(|attr| {
- attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg")
- }));
+ required_cfgs.extend(
+ has_attrs
+ .attrs()
+ .filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))),
+ );
}
}
}
diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs
index 9bfbeeebf7..be4fe763a0 100644
--- a/crates/ide-diagnostics/src/handlers/inactive_code.rs
+++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs
@@ -209,8 +209,8 @@ union FooBar {
#[cfg(true)] fn active() {}
- #[cfg(any(not(true)), false)] fn inactive2() {}
-//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: true is enabled
+ #[cfg(any(not(true), false))] fn inactive2() {}
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: true is enabled and false is disabled
"#,
);
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index 6f4ea70e0a..fb885c2ad1 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -54,8 +54,9 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
let InFile { file_id, value: tokens } =
hir::InMacroFile::new(macro_file, descended).upmap_once(db);
let token = sema.parse_or_expand(file_id).covering_element(tokens[0]).into_token()?;
- let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
+ let attr = token.parent_ancestors().find_map(ast::Meta::cast)?;
let expansions = sema.expand_derive_macro(&attr)?;
+ let ast::Meta::TokenTreeMeta(attr) = attr else { return None };
let idx = attr
.token_tree()?
.token_trees_and_tokens()
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index ce9ec7431a..dcfe4dd41e 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -166,12 +166,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="comment documentation">///</span>
<span class="comment documentation">/// ```</span>
<span class="comment documentation">///</span><span class="none injected"> </span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="keyword attribute">cfg_attr</span><span class="parenthesis attribute">(</span>not<span class="parenthesis attribute">(</span>feature <span class="operator attribute">=</span> <span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="builtin_attr attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="attribute_bracket attribute">]</span>
<span class="comment documentation">/// ```</span>
<span class="comment documentation">///</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="keyword attribute">cfg_attr</span><span class="parenthesis attribute">(</span>feature <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span> <span class="builtin_attr attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="keyword attribute">cfg_attr</span><span class="parenthesis attribute">(</span>not<span class="parenthesis attribute">(</span>feature <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="builtin_attr attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
<span class="comment documentation">///</span><span class="none injected"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="module injected">alloc</span><span class="operator injected">::</span><span class="macro injected">vec</span><span class="macro_bang injected">!</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
<span class="comment documentation">/// ```</span>
<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration public">mix_and_match</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs
index c0cf43a87b..2eeaa25257 100644
--- a/crates/parser/src/grammar/attributes.rs
+++ b/crates/parser/src/grammar/attributes.rs
@@ -40,6 +40,86 @@ fn attr(p: &mut Parser<'_>, inner: bool) {
// #![unsafe]
// #![unsafe =]
+fn cfg_attr_meta(p: &mut Parser<'_>, m: Marker) {
+ // test cfg_attr
+ // #![cfg_attr(not(foo), unsafe(bar()), cfg_attr(all(true, foo = "bar"), baz = "baz"))]
+ p.eat_contextual_kw(T![cfg_attr]);
+ p.bump(T!['(']);
+ cfg_predicate(p);
+ p.expect(T![,]);
+ while !p.at(T![')']) && !p.at(EOF) {
+ meta(p);
+ if !p.eat(T![,]) {
+ break;
+ }
+ }
+ p.expect(T![')']);
+ m.complete(p, CFG_ATTR_META);
+}
+
+const CFG_PREDICATE_FIRST_SET: TokenSet = TokenSet::new(&[T![true], T![false], T![ident]]);
+
+fn cfg_predicate(p: &mut Parser<'_>) {
+ let m = p.start();
+ if p.eat(T![true]) || p.eat(T![false]) {
+ // test cfg_true_false_pred
+ // #![cfg(true)]
+ // #![cfg(false)]
+ m.complete(p, CFG_ATOM);
+ return;
+ }
+ p.expect(T![ident]);
+ if p.eat(T![=]) {
+ if p.at(T![ident]) {
+ // This is required for completion, that inserts an identifier, to work in cases like
+ // `#[cfg(key = $0)]`, and also makes sense on itself.
+
+ // test_err key_ident_cfg_predicate
+ // #![cfg(key = value)]
+ p.err_and_bump("expected a string literal");
+ } else {
+ // test cfg_key_value_pred
+ // #![cfg(key = "value")]
+ p.expect(T![string]);
+ }
+ m.complete(p, CFG_ATOM);
+ } else if p.at(T!['(']) {
+ // test cfg_composite_pred
+ // #![cfg(any(a, all(b = "c", d)))]
+ delimited(
+ p,
+ T!['('],
+ T![')'],
+ T![,],
+ || "expected a cfg predicate".to_owned(),
+ CFG_PREDICATE_FIRST_SET,
+ |p| {
+ if p.at_ts(CFG_PREDICATE_FIRST_SET) {
+ cfg_predicate(p);
+ true
+ } else {
+ false
+ }
+ },
+ );
+ m.complete(p, CFG_COMPOSITE);
+ } else {
+ m.complete(p, CFG_ATOM);
+ }
+}
+
+fn cfg_meta(p: &mut Parser<'_>, m: Marker) {
+ // test cfg_meta
+ // #![cfg(foo)]
+ // #![cfg(foo = "bar",)]
+ p.eat_contextual_kw(T![cfg]);
+ p.bump(T!['(']);
+ cfg_predicate(p);
+ p.eat(T![,]);
+ p.expect(T![')']);
+ m.complete(p, CFG_META);
+}
+
// test metas
// #![simple_ident]
// #![simple::path]
@@ -62,11 +142,23 @@ fn attr(p: &mut Parser<'_>, inner: bool) {
// #![unsafe(simple::path::tt[a b c])]
// #![unsafe(simple::path::tt{a b c})]
pub(super) fn meta(p: &mut Parser<'_>) {
- let meta = p.start();
- let is_unsafe = p.eat(T![unsafe]);
- if is_unsafe {
+ let m = p.start();
+ if p.eat(T![unsafe]) {
p.expect(T!['(']);
+ meta(p);
+ p.expect(T![')']);
+ m.complete(p, UNSAFE_META);
+ return;
+ }
+
+ if p.nth_at(1, T!['(']) {
+ if p.at_contextual_kw(T![cfg_attr]) {
+ return cfg_attr_meta(p, m);
+ } else if p.at_contextual_kw(T![cfg]) {
+ return cfg_meta(p, m);
+ }
}
+
paths::attr_path(p);
match p.current() {
@@ -75,13 +167,14 @@ pub(super) fn meta(p: &mut Parser<'_>) {
if expressions::expr(p).is_none() {
p.error("expected expression");
}
+ m.complete(p, KEY_VALUE_META);
+ }
+ T!['('] | T!['['] | T!['{'] => {
+ items::token_tree(p);
+ m.complete(p, TOKEN_TREE_META);
+ }
+ _ => {
+ m.complete(p, PATH_META);
}
- T!['('] | T!['['] | T!['{'] => items::token_tree(p),
- _ => {}
- }
- if is_unsafe {
- p.expect(T![')']);
}
-
- meta.complete(p, META);
}
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index a2295e4495..9cd48f2aa4 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -116,6 +116,8 @@ pub enum SyntaxKind {
AWAIT_KW,
BIKESHED_KW,
BUILTIN_KW,
+ CFG_ATTR_KW,
+ CFG_KW,
CLOBBER_ABI_KW,
DEFAULT_KW,
DYN_KW,
@@ -186,6 +188,10 @@ pub enum SyntaxKind {
BREAK_EXPR,
CALL_EXPR,
CAST_EXPR,
+ CFG_ATOM,
+ CFG_ATTR_META,
+ CFG_COMPOSITE,
+ CFG_META,
CLOSURE_EXPR,
CONST,
CONST_ARG,
@@ -216,6 +222,7 @@ pub enum SyntaxKind {
INDEX_EXPR,
INFER_TYPE,
ITEM_LIST,
+ KEY_VALUE_META,
LABEL,
LET_ELSE,
LET_EXPR,
@@ -238,7 +245,6 @@ pub enum SyntaxKind {
MATCH_ARM_LIST,
MATCH_EXPR,
MATCH_GUARD,
- META,
METHOD_CALL_EXPR,
MODULE,
NAME,
@@ -254,6 +260,7 @@ pub enum SyntaxKind {
PAREN_TYPE,
PATH,
PATH_EXPR,
+ PATH_META,
PATH_PAT,
PATH_SEGMENT,
PATH_TYPE,
@@ -285,6 +292,7 @@ pub enum SyntaxKind {
STMT_LIST,
STRUCT,
TOKEN_TREE,
+ TOKEN_TREE_META,
TRAIT,
TRY_BLOCK_MODIFIER,
TRY_EXPR,
@@ -302,6 +310,7 @@ pub enum SyntaxKind {
TYPE_PARAM,
UNDERSCORE_EXPR,
UNION,
+ UNSAFE_META,
USE,
USE_BOUND_GENERIC_ARGS,
USE_TREE,
@@ -360,6 +369,10 @@ impl SyntaxKind {
| BREAK_EXPR
| CALL_EXPR
| CAST_EXPR
+ | CFG_ATOM
+ | CFG_ATTR_META
+ | CFG_COMPOSITE
+ | CFG_META
| CLOSURE_EXPR
| CONST
| CONST_ARG
@@ -390,6 +403,7 @@ impl SyntaxKind {
| INDEX_EXPR
| INFER_TYPE
| ITEM_LIST
+ | KEY_VALUE_META
| LABEL
| LET_ELSE
| LET_EXPR
@@ -412,7 +426,6 @@ impl SyntaxKind {
| MATCH_ARM_LIST
| MATCH_EXPR
| MATCH_GUARD
- | META
| METHOD_CALL_EXPR
| MODULE
| NAME
@@ -428,6 +441,7 @@ impl SyntaxKind {
| PAREN_TYPE
| PATH
| PATH_EXPR
+ | PATH_META
| PATH_PAT
| PATH_SEGMENT
| PATH_TYPE
@@ -459,6 +473,7 @@ impl SyntaxKind {
| STMT_LIST
| STRUCT
| TOKEN_TREE
+ | TOKEN_TREE_META
| TRAIT
| TRY_BLOCK_MODIFIER
| TRY_EXPR
@@ -476,6 +491,7 @@ impl SyntaxKind {
| TYPE_PARAM
| UNDERSCORE_EXPR
| UNION
+ | UNSAFE_META
| USE
| USE_BOUND_GENERIC_ARGS
| USE_TREE
@@ -601,6 +617,8 @@ impl SyntaxKind {
AUTO_KW => "auto",
BIKESHED_KW => "bikeshed",
BUILTIN_KW => "builtin",
+ CFG_KW => "cfg",
+ CFG_ATTR_KW => "cfg_attr",
CLOBBER_ABI_KW => "clobber_abi",
DEFAULT_KW => "default",
DYN_KW => "dyn",
@@ -704,6 +722,8 @@ impl SyntaxKind {
AUTO_KW => true,
BIKESHED_KW => true,
BUILTIN_KW => true,
+ CFG_KW => true,
+ CFG_ATTR_KW => true,
CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
DYN_KW if edition < Edition::Edition2018 => true,
@@ -795,6 +815,8 @@ impl SyntaxKind {
AUTO_KW => true,
BIKESHED_KW => true,
BUILTIN_KW => true,
+ CFG_KW => true,
+ CFG_ATTR_KW => true,
CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
DYN_KW if edition < Edition::Edition2018 => true,
@@ -949,6 +971,8 @@ impl SyntaxKind {
"auto" => AUTO_KW,
"bikeshed" => BIKESHED_KW,
"builtin" => BUILTIN_KW,
+ "cfg" => CFG_KW,
+ "cfg_attr" => CFG_ATTR_KW,
"clobber_abi" => CLOBBER_ABI_KW,
"default" => DEFAULT_KW,
"dyn" if edition < Edition::Edition2018 => DYN_KW,
@@ -1121,6 +1145,8 @@ macro_rules ! T_ {
[auto] => { $ crate :: SyntaxKind :: AUTO_KW };
[bikeshed] => { $ crate :: SyntaxKind :: BIKESHED_KW };
[builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW };
+ [cfg] => { $ crate :: SyntaxKind :: CFG_KW };
+ [cfg_attr] => { $ crate :: SyntaxKind :: CFG_ATTR_KW };
[clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW };
[default] => { $ crate :: SyntaxKind :: DEFAULT_KW };
[dyn] => { $ crate :: SyntaxKind :: DYN_KW };
diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs
index 01fc172ed9..71978390df 100644
--- a/crates/parser/test_data/generated/runner.rs
+++ b/crates/parser/test_data/generated/runner.rs
@@ -87,6 +87,22 @@ mod ok {
#[test]
fn cast_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/cast_expr.rs"); }
#[test]
+ fn cfg_attr() { run_and_expect_no_errors("test_data/parser/inline/ok/cfg_attr.rs"); }
+ #[test]
+ fn cfg_composite_pred() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/cfg_composite_pred.rs");
+ }
+ #[test]
+ fn cfg_key_value_pred() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/cfg_key_value_pred.rs");
+ }
+ #[test]
+ fn cfg_meta() { run_and_expect_no_errors("test_data/parser/inline/ok/cfg_meta.rs"); }
+ #[test]
+ fn cfg_true_false_pred() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/cfg_true_false_pred.rs");
+ }
+ #[test]
fn closure_binder() {
run_and_expect_no_errors("test_data/parser/inline/ok/closure_binder.rs");
}
@@ -826,6 +842,10 @@ mod err {
);
}
#[test]
+ fn key_ident_cfg_predicate() {
+ run_and_expect_errors("test_data/parser/inline/err/key_ident_cfg_predicate.rs");
+ }
+ #[test]
fn let_else_right_curly_brace() {
run_and_expect_errors("test_data/parser/inline/err/let_else_right_curly_brace.rs");
}
diff --git a/crates/parser/test_data/parser/err/0005_attribute_recover.rast b/crates/parser/test_data/parser/err/0005_attribute_recover.rast
index 77b4d06321..cf45dcf522 100644
--- a/crates/parser/test_data/parser/err/0005_attribute_recover.rast
+++ b/crates/parser/test_data/parser/err/0005_attribute_recover.rast
@@ -3,7 +3,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -37,7 +37,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast b/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast
index b657e98341..2334b730e4 100644
--- a/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast
+++ b/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast
@@ -136,15 +136,12 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "test"
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
ATTR
diff --git a/crates/parser/test_data/parser/err/0033_match_arms_outer_attrs.rast b/crates/parser/test_data/parser/err/0033_match_arms_outer_attrs.rast
index b5bc3d84df..acacee2348 100644
--- a/crates/parser/test_data/parser/err/0033_match_arms_outer_attrs.rast
+++ b/crates/parser/test_data/parser/err/0033_match_arms_outer_attrs.rast
@@ -48,15 +48,12 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "test"
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
R_CURLY "}"
diff --git a/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rast b/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rast
new file mode 100644
index 0000000000..de5fc7d5bd
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rast
@@ -0,0 +1,19 @@
+SOURCE_FILE
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
+ IDENT "key"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ ERROR
+ IDENT "value"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+error 13: expected a string literal
diff --git a/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rs b/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rs
new file mode 100644
index 0000000000..9a981bf939
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rs
@@ -0,0 +1 @@
+#![cfg(key = value)]
diff --git a/crates/parser/test_data/parser/inline/err/meta_recovery.rast b/crates/parser/test_data/parser/inline/err/meta_recovery.rast
index b5c16e0798..9e456c9855 100644
--- a/crates/parser/test_data/parser/inline/err/meta_recovery.rast
+++ b/crates/parser/test_data/parser/inline/err/meta_recovery.rast
@@ -3,14 +3,14 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ PATH_META
R_BRACK "]"
WHITESPACE "\n"
ATTR
POUND "#"
BANG "!"
L_BRACK "["
- META
+ KEY_VALUE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -24,7 +24,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ PATH_META
PATH
PATH
PATH_SEGMENT
@@ -37,7 +37,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ KEY_VALUE_META
PATH
PATH
PATH_SEGMENT
@@ -52,18 +52,20 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
+ PATH_META
R_BRACK "]"
WHITESPACE "\n"
ATTR
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
WHITESPACE " "
- EQ "="
+ KEY_VALUE_META
+ EQ "="
R_BRACK "]"
WHITESPACE "\n"
error 3: expected identifier, `self`, `super`, `crate`, or `Self`
@@ -77,7 +79,7 @@ error 41: expected L_PAREN
error 41: expected identifier, `self`, `super`, `crate`, or `Self`
error 41: expected R_PAREN
error 52: expected L_PAREN
-error 52: expected identifier, `self`, `super`, `crate`, or `Self`
+error 53: expected identifier, `self`, `super`, `crate`, or `Self`
error 54: expected expression
error 54: expected expression
error 54: expected R_PAREN
diff --git a/crates/parser/test_data/parser/inline/ok/arg_with_attr.rast b/crates/parser/test_data/parser/inline/ok/arg_with_attr.rast
index ae1074c368..672f2c2f7f 100644
--- a/crates/parser/test_data/parser/inline/ok/arg_with_attr.rast
+++ b/crates/parser/test_data/parser/inline/ok/arg_with_attr.rast
@@ -24,7 +24,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/array_attrs.rast b/crates/parser/test_data/parser/inline/ok/array_attrs.rast
index 6eb8af3311..2812bbf71b 100644
--- a/crates/parser/test_data/parser/inline/ok/array_attrs.rast
+++ b/crates/parser/test_data/parser/inline/ok/array_attrs.rast
@@ -31,15 +31,12 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "test"
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE " "
INT_NUMBER "2"
diff --git a/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast b/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast
index 9cb3c8a5c3..7c6dbf65cf 100644
--- a/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast
+++ b/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast
@@ -15,7 +15,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast b/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast
index 81b7f2b3cb..248e6d1360 100644
--- a/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast
+++ b/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast
@@ -17,7 +17,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -39,7 +39,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -61,7 +61,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -71,7 +71,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -87,7 +87,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_attr.rast b/crates/parser/test_data/parser/inline/ok/cfg_attr.rast
new file mode 100644
index 0000000000..9af94f447d
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_attr.rast
@@ -0,0 +1,63 @@
+SOURCE_FILE
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_ATTR_META
+ CFG_ATTR_KW "cfg_attr"
+ L_PAREN "("
+ CFG_COMPOSITE
+ IDENT "not"
+ L_PAREN "("
+ CFG_ATOM
+ IDENT "foo"
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE " "
+ UNSAFE_META
+ UNSAFE_KW "unsafe"
+ L_PAREN "("
+ TOKEN_TREE_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "bar"
+ TOKEN_TREE
+ L_PAREN "("
+ R_PAREN ")"
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE " "
+ CFG_ATTR_META
+ CFG_ATTR_KW "cfg_attr"
+ L_PAREN "("
+ CFG_COMPOSITE
+ IDENT "all"
+ L_PAREN "("
+ CFG_ATOM
+ TRUE_KW "true"
+ COMMA ","
+ WHITESPACE " "
+ CFG_ATOM
+ IDENT "foo"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ STRING "\"bar\""
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE " "
+ KEY_VALUE_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "baz"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ STRING "\"baz\""
+ R_PAREN ")"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_attr.rs b/crates/parser/test_data/parser/inline/ok/cfg_attr.rs
new file mode 100644
index 0000000000..5fe2776144
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_attr.rs
@@ -0,0 +1 @@
+#![cfg_attr(not(foo), unsafe(bar()), cfg_attr(all(true, foo = "bar"), baz = "baz"))]
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rast b/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rast
new file mode 100644
index 0000000000..89d06d134f
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rast
@@ -0,0 +1,33 @@
+SOURCE_FILE
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_COMPOSITE
+ IDENT "any"
+ L_PAREN "("
+ CFG_ATOM
+ IDENT "a"
+ COMMA ","
+ WHITESPACE " "
+ CFG_COMPOSITE
+ IDENT "all"
+ L_PAREN "("
+ CFG_ATOM
+ IDENT "b"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ STRING "\"c\""
+ COMMA ","
+ WHITESPACE " "
+ CFG_ATOM
+ IDENT "d"
+ R_PAREN ")"
+ R_PAREN ")"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rs b/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rs
new file mode 100644
index 0000000000..7d830c1288
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rs
@@ -0,0 +1 @@
+#![cfg(any(a, all(b = "c", d)))]
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rast b/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rast
new file mode 100644
index 0000000000..e48d39bf55
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rast
@@ -0,0 +1,17 @@
+SOURCE_FILE
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
+ IDENT "key"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ STRING "\"value\""
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rs b/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rs
new file mode 100644
index 0000000000..dc194ed86b
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rs
@@ -0,0 +1 @@
+#![cfg(key = "value")]
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_meta.rast b/crates/parser/test_data/parser/inline/ok/cfg_meta.rast
new file mode 100644
index 0000000000..f024cfd1aa
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_meta.rast
@@ -0,0 +1,30 @@
+SOURCE_FILE
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
+ IDENT "foo"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
+ IDENT "foo"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ STRING "\"bar\""
+ COMMA ","
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_meta.rs b/crates/parser/test_data/parser/inline/ok/cfg_meta.rs
new file mode 100644
index 0000000000..ef0030e75f
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_meta.rs
@@ -0,0 +1,2 @@
+#![cfg(foo)]
+#![cfg(foo = "bar",)]
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rast b/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rast
new file mode 100644
index 0000000000..e33595a93d
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rast
@@ -0,0 +1,25 @@
+SOURCE_FILE
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
+ TRUE_KW "true"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
+ FALSE_KW "false"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rs b/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rs
new file mode 100644
index 0000000000..473582164a
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rs
@@ -0,0 +1,2 @@
+#![cfg(true)]
+#![cfg(false)]
diff --git a/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast b/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast
index 28a216e873..5567a53c56 100644
--- a/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast
+++ b/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast
@@ -10,7 +10,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -25,7 +25,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast b/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast
index 6fd9f42467..edb0438733 100644
--- a/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast
+++ b/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast
@@ -26,7 +26,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -41,7 +41,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -56,7 +56,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast b/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast
index 0f7580c1a3..321db782d1 100644
--- a/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast
+++ b/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast
@@ -26,19 +26,16 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "feature"
WHITESPACE " "
EQ "="
WHITESPACE " "
STRING "\"some\""
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
WILDCARD_PAT
@@ -55,19 +52,16 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "feature"
WHITESPACE " "
EQ "="
WHITESPACE " "
STRING "\"other\""
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
WILDCARD_PAT
@@ -84,55 +78,46 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "feature"
WHITESPACE " "
EQ "="
WHITESPACE " "
STRING "\"many\""
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "feature"
WHITESPACE " "
EQ "="
WHITESPACE " "
STRING "\"attributes\""
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "feature"
WHITESPACE " "
EQ "="
WHITESPACE " "
STRING "\"before\""
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
WILDCARD_PAT
diff --git a/crates/parser/test_data/parser/inline/ok/metas.rast b/crates/parser/test_data/parser/inline/ok/metas.rast
index b1ac60b530..6360552a6f 100644
--- a/crates/parser/test_data/parser/inline/ok/metas.rast
+++ b/crates/parser/test_data/parser/inline/ok/metas.rast
@@ -3,7 +3,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -14,7 +14,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ PATH_META
PATH
PATH
PATH_SEGMENT
@@ -30,7 +30,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ KEY_VALUE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -46,7 +46,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ KEY_VALUE_META
PATH
PATH
PATH
@@ -72,7 +72,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -91,7 +91,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -110,7 +110,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -129,7 +129,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH
PATH
@@ -158,7 +158,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH
PATH
@@ -187,7 +187,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH
PATH
@@ -216,13 +216,14 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "simple_ident"
+ PATH_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple_ident"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -230,18 +231,19 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
+ PATH_META
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "simple"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "path"
+ IDENT "path"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -249,18 +251,19 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "simple_ident_expr"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- LITERAL
- STRING "\"\""
+ KEY_VALUE_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple_ident_expr"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ STRING "\"\""
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -268,28 +271,29 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
+ KEY_VALUE_META
PATH
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "simple"
+ IDENT "path"
COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "path"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "Expr"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- LITERAL
- STRING "\"\""
+ IDENT "Expr"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ STRING "\"\""
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -297,21 +301,22 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "simple_ident_tt"
- TOKEN_TREE
- L_PAREN "("
- IDENT "a"
- WHITESPACE " "
- IDENT "b"
- WHITESPACE " "
- IDENT "c"
- R_PAREN ")"
+ TOKEN_TREE_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple_ident_tt"
+ TOKEN_TREE
+ L_PAREN "("
+ IDENT "a"
+ WHITESPACE " "
+ IDENT "b"
+ WHITESPACE " "
+ IDENT "c"
+ R_PAREN ")"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -319,21 +324,22 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "simple_ident_tt"
- TOKEN_TREE
- L_BRACK "["
- IDENT "a"
- WHITESPACE " "
- IDENT "b"
- WHITESPACE " "
- IDENT "c"
- R_BRACK "]"
+ TOKEN_TREE_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple_ident_tt"
+ TOKEN_TREE
+ L_BRACK "["
+ IDENT "a"
+ WHITESPACE " "
+ IDENT "b"
+ WHITESPACE " "
+ IDENT "c"
+ R_BRACK "]"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -341,21 +347,22 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "simple_ident_tt"
- TOKEN_TREE
- L_CURLY "{"
- IDENT "a"
- WHITESPACE " "
- IDENT "b"
- WHITESPACE " "
- IDENT "c"
- R_CURLY "}"
+ TOKEN_TREE_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple_ident_tt"
+ TOKEN_TREE
+ L_CURLY "{"
+ IDENT "a"
+ WHITESPACE " "
+ IDENT "b"
+ WHITESPACE " "
+ IDENT "c"
+ R_CURLY "}"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -363,31 +370,32 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
+ TOKEN_TREE_META
PATH
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "simple"
+ IDENT "path"
COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "path"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "tt"
- TOKEN_TREE
- L_PAREN "("
- IDENT "a"
- WHITESPACE " "
- IDENT "b"
- WHITESPACE " "
- IDENT "c"
- R_PAREN ")"
+ IDENT "tt"
+ TOKEN_TREE
+ L_PAREN "("
+ IDENT "a"
+ WHITESPACE " "
+ IDENT "b"
+ WHITESPACE " "
+ IDENT "c"
+ R_PAREN ")"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -395,31 +403,32 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
+ TOKEN_TREE_META
PATH
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "simple"
+ IDENT "path"
COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "path"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "tt"
- TOKEN_TREE
- L_BRACK "["
- IDENT "a"
- WHITESPACE " "
- IDENT "b"
- WHITESPACE " "
- IDENT "c"
- R_BRACK "]"
+ IDENT "tt"
+ TOKEN_TREE
+ L_BRACK "["
+ IDENT "a"
+ WHITESPACE " "
+ IDENT "b"
+ WHITESPACE " "
+ IDENT "c"
+ R_BRACK "]"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -427,31 +436,32 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
+ TOKEN_TREE_META
PATH
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "simple"
+ IDENT "path"
COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "path"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "tt"
- TOKEN_TREE
- L_CURLY "{"
- IDENT "a"
- WHITESPACE " "
- IDENT "b"
- WHITESPACE " "
- IDENT "c"
- R_CURLY "}"
+ IDENT "tt"
+ TOKEN_TREE
+ L_CURLY "{"
+ IDENT "a"
+ WHITESPACE " "
+ IDENT "b"
+ WHITESPACE " "
+ IDENT "c"
+ R_CURLY "}"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/param_outer_arg.rast b/crates/parser/test_data/parser/inline/ok/param_outer_arg.rast
index c63ea020a3..7495ba7b31 100644
--- a/crates/parser/test_data/parser/inline/ok/param_outer_arg.rast
+++ b/crates/parser/test_data/parser/inline/ok/param_outer_arg.rast
@@ -10,7 +10,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/record_field_attrs.rast b/crates/parser/test_data/parser/inline/ok/record_field_attrs.rast
index 639ee0eb77..cfa2694fd1 100644
--- a/crates/parser/test_data/parser/inline/ok/record_field_attrs.rast
+++ b/crates/parser/test_data/parser/inline/ok/record_field_attrs.rast
@@ -12,7 +12,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast b/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast
index a1df70841e..717dee8c9f 100644
--- a/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast
+++ b/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast
@@ -25,15 +25,12 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "test"
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE " "
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/record_pat_field.rast b/crates/parser/test_data/parser/inline/ok/record_pat_field.rast
index f3d2fde466..7fba529be9 100644
--- a/crates/parser/test_data/parser/inline/ok/record_pat_field.rast
+++ b/crates/parser/test_data/parser/inline/ok/record_pat_field.rast
@@ -88,18 +88,14 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_COMPOSITE
IDENT "any"
- TOKEN_TREE
- L_PAREN "("
- R_PAREN ")"
+ L_PAREN "("
R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE " "
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast b/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast
index f69ae1d644..af5b82b889 100644
--- a/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast
+++ b/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast
@@ -146,18 +146,14 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_COMPOSITE
IDENT "any"
- TOKEN_TREE
- L_PAREN "("
- R_PAREN ")"
+ L_PAREN "("
R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE " "
DOT2 ".."
diff --git a/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast b/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast
index db583f7d52..3a163e5b8e 100644
--- a/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast
+++ b/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast
@@ -10,7 +10,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/tuple_attrs.rast b/crates/parser/test_data/parser/inline/ok/tuple_attrs.rast
index 39857b23c6..76954927d5 100644
--- a/crates/parser/test_data/parser/inline/ok/tuple_attrs.rast
+++ b/crates/parser/test_data/parser/inline/ok/tuple_attrs.rast
@@ -34,15 +34,12 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "test"
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE " "
INT_NUMBER "2"
diff --git a/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast b/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast
index 1699602f4f..1f7100c46d 100644
--- a/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast
+++ b/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast
@@ -11,7 +11,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0006_inner_attributes.rast b/crates/parser/test_data/parser/ok/0006_inner_attributes.rast
index cb63ba80e7..ddab028c06 100644
--- a/crates/parser/test_data/parser/ok/0006_inner_attributes.rast
+++ b/crates/parser/test_data/parser/ok/0006_inner_attributes.rast
@@ -3,7 +3,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -14,7 +14,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -29,7 +29,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -44,7 +44,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -89,7 +89,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -104,7 +104,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -123,7 +123,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -138,7 +138,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -153,7 +153,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -175,7 +175,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0008_mod_item.rast b/crates/parser/test_data/parser/ok/0008_mod_item.rast
index adee67181b..cb6da8717d 100644
--- a/crates/parser/test_data/parser/ok/0008_mod_item.rast
+++ b/crates/parser/test_data/parser/ok/0008_mod_item.rast
@@ -48,7 +48,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0011_outer_attribute.rast b/crates/parser/test_data/parser/ok/0011_outer_attribute.rast
index dbb9bc54da..47a5cfce08 100644
--- a/crates/parser/test_data/parser/ok/0011_outer_attribute.rast
+++ b/crates/parser/test_data/parser/ok/0011_outer_attribute.rast
@@ -3,21 +3,18 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "test"
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -41,7 +38,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ KEY_VALUE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0017_attr_trailing_comma.rast b/crates/parser/test_data/parser/ok/0017_attr_trailing_comma.rast
index 7c914e2542..c5d054702f 100644
--- a/crates/parser/test_data/parser/ok/0017_attr_trailing_comma.rast
+++ b/crates/parser/test_data/parser/ok/0017_attr_trailing_comma.rast
@@ -3,7 +3,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0035_weird_exprs.rast b/crates/parser/test_data/parser/ok/0035_weird_exprs.rast
index 318d492ab4..15ce6c70be 100644
--- a/crates/parser/test_data/parser/ok/0035_weird_exprs.rast
+++ b/crates/parser/test_data/parser/ok/0035_weird_exprs.rast
@@ -11,7 +11,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -26,7 +26,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -41,7 +41,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -56,7 +56,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -71,7 +71,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ KEY_VALUE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0044_let_attrs.rast b/crates/parser/test_data/parser/ok/0044_let_attrs.rast
index f3c20337e4..fcdc1a9895 100644
--- a/crates/parser/test_data/parser/ok/0044_let_attrs.rast
+++ b/crates/parser/test_data/parser/ok/0044_let_attrs.rast
@@ -18,19 +18,16 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "feature"
WHITESPACE " "
EQ "="
WHITESPACE " "
STRING "\"backtrace\""
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
LET_KW "let"
diff --git a/crates/parser/test_data/parser/ok/0045_block_attrs.rast b/crates/parser/test_data/parser/ok/0045_block_attrs.rast
index c22d99f1ae..f26bb85df2 100644
--- a/crates/parser/test_data/parser/ok/0045_block_attrs.rast
+++ b/crates/parser/test_data/parser/ok/0045_block_attrs.rast
@@ -16,7 +16,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -38,7 +38,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -53,7 +53,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -77,7 +77,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -119,7 +119,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -211,7 +211,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast b/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast
index 4eb51cfdf0..3d33eb4ff7 100644
--- a/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast
+++ b/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast
@@ -14,7 +14,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast b/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast
index eafee90db4..24d4392282 100644
--- a/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast
+++ b/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast
@@ -10,7 +10,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -20,7 +20,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -55,7 +55,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -116,7 +116,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -158,7 +158,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -181,7 +181,7 @@ SOURCE_FILE
POUND "#"
WHITESPACE " "
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -228,7 +228,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -255,7 +255,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -282,7 +282,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -316,7 +316,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -352,7 +352,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -389,7 +389,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -422,7 +422,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -456,7 +456,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast b/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast
index b94d43beb3..c300b7af50 100644
--- a/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast
+++ b/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast
@@ -5,7 +5,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0062_macro_2.0.rast b/crates/parser/test_data/parser/ok/0062_macro_2.0.rast
index 1415a866b6..b92d78e5bd 100644
--- a/crates/parser/test_data/parser/ok/0062_macro_2.0.rast
+++ b/crates/parser/test_data/parser/ok/0062_macro_2.0.rast
@@ -54,7 +54,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0063_variadic_fun.rast b/crates/parser/test_data/parser/ok/0063_variadic_fun.rast
index e36399123b..f1c6d2efeb 100644
--- a/crates/parser/test_data/parser/ok/0063_variadic_fun.rast
+++ b/crates/parser/test_data/parser/ok/0063_variadic_fun.rast
@@ -96,15 +96,12 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "never"
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE " "
SLICE_PAT
diff --git a/crates/parser/test_data/parser/ok/0070_expr_attr_placement.rast b/crates/parser/test_data/parser/ok/0070_expr_attr_placement.rast
index 3d00b27ab8..5229b97eb2 100644
--- a/crates/parser/test_data/parser/ok/0070_expr_attr_placement.rast
+++ b/crates/parser/test_data/parser/ok/0070_expr_attr_placement.rast
@@ -19,7 +19,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -39,7 +39,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0071_stmt_attr_placement.rast b/crates/parser/test_data/parser/ok/0071_stmt_attr_placement.rast
index 1cafc775cd..c0685448f2 100644
--- a/crates/parser/test_data/parser/ok/0071_stmt_attr_placement.rast
+++ b/crates/parser/test_data/parser/ok/0071_stmt_attr_placement.rast
@@ -17,7 +17,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -31,7 +31,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -56,7 +56,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/rust-analyzer/src/target_spec.rs b/crates/rust-analyzer/src/target_spec.rs
index 01196b80cd..5bdc9d8ca3 100644
--- a/crates/rust-analyzer/src/target_spec.rs
+++ b/crates/rust-analyzer/src/target_spec.rs
@@ -381,23 +381,13 @@ mod tests {
SmolStr,
ast::{self, AstNode},
};
- use syntax_bridge::{
- DocCommentDesugarMode,
- dummy_test_span_utils::{DUMMY, DummyTestSpanMap},
- syntax_node_to_token_tree,
- };
fn check(cfg: &str, expected_features: &[&str]) {
let cfg_expr = {
let source_file = ast::SourceFile::parse(cfg, Edition::CURRENT).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,
- DocCommentDesugarMode::Mbe,
- );
- CfgExpr::parse(&tt)
+ let cfg_predicate =
+ source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap();
+ CfgExpr::parse_from_ast(cfg_predicate)
};
let mut features = vec![];
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 8909fb423c..e65836ed8d 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -21,6 +21,7 @@ rustc-literal-escaper.workspace = true
smol_str.workspace = true
triomphe.workspace = true
tracing.workspace = true
+smallvec.workspace = true
parser.workspace = true
stdx.workspace = true
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 3113fc7430..324b2bbd58 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -126,9 +126,41 @@ MacroStmts =
Attr =
'#' '!'? '[' Meta ']'
+CfgAttrMeta =
+ 'cfg_attr' '(' CfgPredicate ',' (Meta (',' Meta)* ','?) ')'
+
+CfgMeta =
+ 'cfg' '(' CfgPredicate ','? ')'
+
+CfgPredicate =
+ CfgAtom
+| CfgComposite
+
+CfgAtom =
+ ('#ident' | 'true' | 'false') ('=' '@string')?
+
+CfgComposite =
+ keyword:'#ident' '(' (CfgPredicate (',' CfgPredicate)* ','?) ')'
+
Meta =
- 'unsafe' '(' Path ('=' Expr | TokenTree)? ')'
-| Path ('=' Expr | TokenTree)?
+ CfgAttrMeta
+| CfgMeta
+| UnsafeMeta
+| PathMeta
+| KeyValueMeta
+| TokenTreeMeta
+
+UnsafeMeta =
+ 'unsafe' '(' Meta ')'
+
+PathMeta =
+ Path
+
+KeyValueMeta =
+ Path '=' Expr
+
+TokenTreeMeta =
+ Path TokenTree
//*************************//
// Items //
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index 5d67fd4491..dc592a4372 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -25,9 +25,9 @@ pub use self::{
expr_ext::{ArrayExprKind, BlockModifier, CallableExpr, ElseBranch, LiteralKind},
generated::{nodes::*, tokens::*},
node_ext::{
- AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind,
- SlicePatComponents, StructKind, TokenTreeChildren, TypeBoundKind, TypeOrConstParam,
- VisibilityKind,
+ AttrKind, CfgAtomKey, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind,
+ SelfParamKind, SlicePatComponents, StructKind, TokenTreeChildren, TypeBoundKind,
+ TypeOrConstParam, VisibilityKind,
},
operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
token_ext::{
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 7334de0fd9..cd7f6a018a 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -377,6 +377,68 @@ impl CastExpr {
#[inline]
pub fn as_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![as]) }
}
+pub struct CfgAtom {
+ pub(crate) syntax: SyntaxNode,
+}
+impl CfgAtom {
+ #[inline]
+ pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+ #[inline]
+ pub fn false_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![false]) }
+ #[inline]
+ pub fn ident_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![ident]) }
+ #[inline]
+ pub fn string_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![string]) }
+ #[inline]
+ pub fn true_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![true]) }
+}
+pub struct CfgAttrMeta {
+ pub(crate) syntax: SyntaxNode,
+}
+impl CfgAttrMeta {
+ #[inline]
+ pub fn cfg_predicate(&self) -> Option<CfgPredicate> { support::child(&self.syntax) }
+ #[inline]
+ pub fn metas(&self) -> AstChildren<Meta> { support::children(&self.syntax) }
+ #[inline]
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+ #[inline]
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+ #[inline]
+ pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+ #[inline]
+ pub fn cfg_attr_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![cfg_attr])
+ }
+}
+pub struct CfgComposite {
+ pub(crate) syntax: SyntaxNode,
+}
+impl CfgComposite {
+ #[inline]
+ pub fn cfg_predicates(&self) -> AstChildren<CfgPredicate> { support::children(&self.syntax) }
+ #[inline]
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+ #[inline]
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+ #[inline]
+ pub fn keyword(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![ident]) }
+}
+pub struct CfgMeta {
+ pub(crate) syntax: SyntaxNode,
+}
+impl CfgMeta {
+ #[inline]
+ pub fn cfg_predicate(&self) -> Option<CfgPredicate> { support::child(&self.syntax) }
+ #[inline]
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+ #[inline]
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+ #[inline]
+ pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+ #[inline]
+ pub fn cfg_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![cfg]) }
+}
pub struct ClosureExpr {
pub(crate) syntax: SyntaxNode,
}
@@ -783,6 +845,17 @@ impl ItemList {
#[inline]
pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) }
}
+pub struct KeyValueMeta {
+ pub(crate) syntax: SyntaxNode,
+}
+impl KeyValueMeta {
+ #[inline]
+ pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+ #[inline]
+ pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
+ #[inline]
+ pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+}
pub struct Label {
pub(crate) syntax: SyntaxNode,
}
@@ -1012,25 +1085,6 @@ impl MatchGuard {
#[inline]
pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) }
}
-pub struct Meta {
- pub(crate) syntax: SyntaxNode,
-}
-impl Meta {
- #[inline]
- pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
- #[inline]
- pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
- #[inline]
- pub fn token_tree(&self) -> Option<TokenTree> { support::child(&self.syntax) }
- #[inline]
- pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
- #[inline]
- pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
- #[inline]
- pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
- #[inline]
- pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
-}
pub struct MethodCallExpr {
pub(crate) syntax: SyntaxNode,
}
@@ -1225,6 +1279,13 @@ impl PathExpr {
#[inline]
pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
}
+pub struct PathMeta {
+ pub(crate) syntax: SyntaxNode,
+}
+impl PathMeta {
+ #[inline]
+ pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
+}
pub struct PathPat {
pub(crate) syntax: SyntaxNode,
}
@@ -1607,6 +1668,15 @@ impl TokenTree {
#[inline]
pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) }
}
+pub struct TokenTreeMeta {
+ pub(crate) syntax: SyntaxNode,
+}
+impl TokenTreeMeta {
+ #[inline]
+ pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
+ #[inline]
+ pub fn token_tree(&self) -> Option<TokenTree> { support::child(&self.syntax) }
+}
pub struct Trait {
pub(crate) syntax: SyntaxNode,
}
@@ -1834,6 +1904,19 @@ impl Union {
#[inline]
pub fn union_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![union]) }
}
+pub struct UnsafeMeta {
+ pub(crate) syntax: SyntaxNode,
+}
+impl UnsafeMeta {
+ #[inline]
+ pub fn meta(&self) -> Option<Meta> { support::child(&self.syntax) }
+ #[inline]
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+ #[inline]
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+ #[inline]
+ pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
+}
pub struct Use {
pub(crate) syntax: SyntaxNode,
}
@@ -2025,6 +2108,12 @@ impl ast::HasAttrs for AssocItem {}
impl ast::HasDocComments for AssocItem {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum CfgPredicate {
+ CfgAtom(CfgAtom),
+ CfgComposite(CfgComposite),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Expr {
ArrayExpr(ArrayExpr),
AsmExpr(AsmExpr),
@@ -2119,6 +2208,16 @@ pub enum Item {
impl ast::HasAttrs for Item {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Meta {
+ CfgAttrMeta(CfgAttrMeta),
+ CfgMeta(CfgMeta),
+ KeyValueMeta(KeyValueMeta),
+ PathMeta(PathMeta),
+ TokenTreeMeta(TokenTreeMeta),
+ UnsafeMeta(UnsafeMeta),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Pat {
BoxPat(BoxPat),
ConstBlockPat(ConstBlockPat),
@@ -3133,6 +3232,134 @@ impl fmt::Debug for CastExpr {
f.debug_struct("CastExpr").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for CfgAtom {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ CFG_ATOM
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_ATOM }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for CfgAtom {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for CfgAtom {}
+impl PartialEq for CfgAtom {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for CfgAtom {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for CfgAtom {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CfgAtom").field("syntax", &self.syntax).finish()
+ }
+}
+impl AstNode for CfgAttrMeta {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ CFG_ATTR_META
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_ATTR_META }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for CfgAttrMeta {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for CfgAttrMeta {}
+impl PartialEq for CfgAttrMeta {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for CfgAttrMeta {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for CfgAttrMeta {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CfgAttrMeta").field("syntax", &self.syntax).finish()
+ }
+}
+impl AstNode for CfgComposite {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ CFG_COMPOSITE
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_COMPOSITE }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for CfgComposite {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for CfgComposite {}
+impl PartialEq for CfgComposite {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for CfgComposite {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for CfgComposite {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CfgComposite").field("syntax", &self.syntax).finish()
+ }
+}
+impl AstNode for CfgMeta {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ CFG_META
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_META }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for CfgMeta {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for CfgMeta {}
+impl PartialEq for CfgMeta {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for CfgMeta {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for CfgMeta {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CfgMeta").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for ClosureExpr {
#[inline]
fn kind() -> SyntaxKind
@@ -4093,6 +4320,38 @@ impl fmt::Debug for ItemList {
f.debug_struct("ItemList").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for KeyValueMeta {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ KEY_VALUE_META
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == KEY_VALUE_META }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for KeyValueMeta {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for KeyValueMeta {}
+impl PartialEq for KeyValueMeta {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for KeyValueMeta {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for KeyValueMeta {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("KeyValueMeta").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for Label {
#[inline]
fn kind() -> SyntaxKind
@@ -4797,38 +5056,6 @@ impl fmt::Debug for MatchGuard {
f.debug_struct("MatchGuard").field("syntax", &self.syntax).finish()
}
}
-impl AstNode for Meta {
- #[inline]
- fn kind() -> SyntaxKind
- where
- Self: Sized,
- {
- META
- }
- #[inline]
- fn can_cast(kind: SyntaxKind) -> bool { kind == META }
- #[inline]
- fn cast(syntax: SyntaxNode) -> Option<Self> {
- if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
- }
- #[inline]
- fn syntax(&self) -> &SyntaxNode { &self.syntax }
-}
-impl hash::Hash for Meta {
- fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
-}
-impl Eq for Meta {}
-impl PartialEq for Meta {
- fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
-}
-impl Clone for Meta {
- fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
-}
-impl fmt::Debug for Meta {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Meta").field("syntax", &self.syntax).finish()
- }
-}
impl AstNode for MethodCallExpr {
#[inline]
fn kind() -> SyntaxKind
@@ -5309,6 +5536,38 @@ impl fmt::Debug for PathExpr {
f.debug_struct("PathExpr").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for PathMeta {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ PATH_META
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_META }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for PathMeta {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for PathMeta {}
+impl PartialEq for PathMeta {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for PathMeta {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for PathMeta {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("PathMeta").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for PathPat {
#[inline]
fn kind() -> SyntaxKind
@@ -6301,6 +6560,38 @@ impl fmt::Debug for TokenTree {
f.debug_struct("TokenTree").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for TokenTreeMeta {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ TOKEN_TREE_META
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == TOKEN_TREE_META }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for TokenTreeMeta {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for TokenTreeMeta {}
+impl PartialEq for TokenTreeMeta {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for TokenTreeMeta {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for TokenTreeMeta {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("TokenTreeMeta").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for Trait {
#[inline]
fn kind() -> SyntaxKind
@@ -6845,6 +7136,38 @@ impl fmt::Debug for Union {
f.debug_struct("Union").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for UnsafeMeta {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ UNSAFE_META
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == UNSAFE_META }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for UnsafeMeta {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for UnsafeMeta {}
+impl PartialEq for UnsafeMeta {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for UnsafeMeta {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for UnsafeMeta {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("UnsafeMeta").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for Use {
#[inline]
fn kind() -> SyntaxKind
@@ -7413,6 +7736,34 @@ impl AstNode for AssocItem {
}
}
}
+impl From<CfgAtom> for CfgPredicate {
+ #[inline]
+ fn from(node: CfgAtom) -> CfgPredicate { CfgPredicate::CfgAtom(node) }
+}
+impl From<CfgComposite> for CfgPredicate {
+ #[inline]
+ fn from(node: CfgComposite) -> CfgPredicate { CfgPredicate::CfgComposite(node) }
+}
+impl AstNode for CfgPredicate {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { matches!(kind, CFG_ATOM | CFG_COMPOSITE) }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ CFG_ATOM => CfgPredicate::CfgAtom(CfgAtom { syntax }),
+ CFG_COMPOSITE => CfgPredicate::CfgComposite(CfgComposite { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ CfgPredicate::CfgAtom(it) => &it.syntax,
+ CfgPredicate::CfgComposite(it) => &it.syntax,
+ }
+ }
+}
impl From<ArrayExpr> for Expr {
#[inline]
fn from(node: ArrayExpr) -> Expr { Expr::ArrayExpr(node) }
@@ -7970,6 +8321,63 @@ impl AstNode for Item {
}
}
}
+impl From<CfgAttrMeta> for Meta {
+ #[inline]
+ fn from(node: CfgAttrMeta) -> Meta { Meta::CfgAttrMeta(node) }
+}
+impl From<CfgMeta> for Meta {
+ #[inline]
+ fn from(node: CfgMeta) -> Meta { Meta::CfgMeta(node) }
+}
+impl From<KeyValueMeta> for Meta {
+ #[inline]
+ fn from(node: KeyValueMeta) -> Meta { Meta::KeyValueMeta(node) }
+}
+impl From<PathMeta> for Meta {
+ #[inline]
+ fn from(node: PathMeta) -> Meta { Meta::PathMeta(node) }
+}
+impl From<TokenTreeMeta> for Meta {
+ #[inline]
+ fn from(node: TokenTreeMeta) -> Meta { Meta::TokenTreeMeta(node) }
+}
+impl From<UnsafeMeta> for Meta {
+ #[inline]
+ fn from(node: UnsafeMeta) -> Meta { Meta::UnsafeMeta(node) }
+}
+impl AstNode for Meta {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool {
+ matches!(
+ kind,
+ CFG_ATTR_META | CFG_META | KEY_VALUE_META | PATH_META | TOKEN_TREE_META | UNSAFE_META
+ )
+ }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ CFG_ATTR_META => Meta::CfgAttrMeta(CfgAttrMeta { syntax }),
+ CFG_META => Meta::CfgMeta(CfgMeta { syntax }),
+ KEY_VALUE_META => Meta::KeyValueMeta(KeyValueMeta { syntax }),
+ PATH_META => Meta::PathMeta(PathMeta { syntax }),
+ TOKEN_TREE_META => Meta::TokenTreeMeta(TokenTreeMeta { syntax }),
+ UNSAFE_META => Meta::UnsafeMeta(UnsafeMeta { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ Meta::CfgAttrMeta(it) => &it.syntax,
+ Meta::CfgMeta(it) => &it.syntax,
+ Meta::KeyValueMeta(it) => &it.syntax,
+ Meta::PathMeta(it) => &it.syntax,
+ Meta::TokenTreeMeta(it) => &it.syntax,
+ Meta::UnsafeMeta(it) => &it.syntax,
+ }
+ }
+}
impl From<BoxPat> for Pat {
#[inline]
fn from(node: BoxPat) -> Pat { Pat::BoxPat(node) }
@@ -9334,6 +9742,11 @@ impl std::fmt::Display for AssocItem {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for CfgPredicate {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -9364,6 +9777,11 @@ impl std::fmt::Display for Item {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for Meta {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for Pat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -9524,6 +9942,26 @@ impl std::fmt::Display for CastExpr {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for CfgAtom {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for CfgAttrMeta {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for CfgComposite {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for CfgMeta {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for ClosureExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -9674,6 +10112,11 @@ impl std::fmt::Display for ItemList {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for KeyValueMeta {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for Label {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -9784,11 +10227,6 @@ impl std::fmt::Display for MatchGuard {
std::fmt::Display::fmt(self.syntax(), f)
}
}
-impl std::fmt::Display for Meta {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- std::fmt::Display::fmt(self.syntax(), f)
- }
-}
impl std::fmt::Display for MethodCallExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -9864,6 +10302,11 @@ impl std::fmt::Display for PathExpr {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for PathMeta {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for PathPat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -10019,6 +10462,11 @@ impl std::fmt::Display for TokenTree {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for TokenTreeMeta {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for Trait {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -10104,6 +10552,11 @@ impl std::fmt::Display for Union {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for UnsafeMeta {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for Use {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 00971569a2..ac02cc9e43 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -1322,6 +1322,18 @@ pub fn meta_path(path: ast::Path) -> ast::Meta {
ast_from_text(&format!("#[{path}]"))
}
+pub fn cfg_attr_meta(
+ predicate: ast::CfgPredicate,
+ inner: impl IntoIterator<Item = ast::Meta>,
+) -> ast::CfgAttrMeta {
+ let inner = inner.into_iter().join(", ");
+ ast_from_text(&format!("#![cfg_attr({predicate}, {inner})]"))
+}
+
+pub fn cfg_flag(flag: &str) -> ast::CfgPredicate {
+ ast_from_text(&format!("#![cfg({flag})]"))
+}
+
pub fn token_tree(
delimiter: SyntaxKind,
tt: impl IntoIterator<Item = NodeOrToken<ast::TokenTree, SyntaxToken>>,
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 3fc3b39fee..03118d01dc 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -8,6 +8,7 @@ use std::{borrow::Cow, fmt, iter::successors};
use itertools::Itertools;
use parser::SyntaxKind;
use rowan::{GreenNodeData, GreenTokenData};
+use smallvec::{SmallVec, smallvec};
use crate::{
NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, TokenText,
@@ -201,53 +202,95 @@ impl AttrKind {
}
}
-impl ast::Attr {
+impl ast::Meta {
pub fn as_simple_atom(&self) -> Option<SmolStr> {
- let meta = self.meta()?;
- if meta.eq_token().is_some() || meta.token_tree().is_some() {
- return None;
- }
- self.simple_name()
+ Some(self.as_simple_path()?.as_single_name_ref()?.text().into())
}
pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> {
- let tt = self.meta()?.token_tree()?;
- Some((self.simple_name()?, tt))
+ let ast::Meta::TokenTreeMeta(meta) = self else { return None };
+ Some((meta.path()?.as_single_name_ref()?.text().into(), meta.token_tree()?))
}
pub fn as_simple_path(&self) -> Option<ast::Path> {
- let meta = self.meta()?;
- if meta.eq_token().is_some() || meta.token_tree().is_some() {
- return None;
- }
- self.path()
+ let ast::Meta::PathMeta(meta) = self else { return None };
+ meta.path()
}
pub fn simple_name(&self) -> Option<SmolStr> {
- let path = self.meta()?.path()?;
- match (path.segment(), path.qualifier()) {
- (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()),
- _ => None,
+ match self {
+ ast::Meta::CfgAttrMeta(_) => Some(SmolStr::new_static("cfg_attr")),
+ ast::Meta::CfgMeta(_) => Some(SmolStr::new_static("cfg")),
+ _ => {
+ let path = self.path()?;
+ match (path.segment(), path.qualifier()) {
+ (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()),
+ _ => None,
+ }
+ }
}
}
- pub fn kind(&self) -> AttrKind {
- match self.excl_token() {
- Some(_) => AttrKind::Inner,
- None => AttrKind::Outer,
+ pub fn path(&self) -> Option<ast::Path> {
+ match self {
+ ast::Meta::CfgAttrMeta(_) | ast::Meta::CfgMeta(_) => None,
+ ast::Meta::KeyValueMeta(it) => it.path(),
+ ast::Meta::PathMeta(it) => it.path(),
+ ast::Meta::TokenTreeMeta(it) => it.path(),
+ ast::Meta::UnsafeMeta(it) => it.meta()?.path(),
}
}
+ /// Includes `cfg_attr()` inner metas (without considering the predicate).
+ pub fn skip_cfg_attrs(self) -> SmallVec<[ast::Meta; 1]> {
+ match self {
+ ast::Meta::CfgAttrMeta(meta) => {
+ meta.metas().flat_map(|meta| meta.skip_cfg_attrs()).collect()
+ }
+ _ => smallvec![self],
+ }
+ }
+
+ /// FIXME: Calling this is almost always incorrect, as `cfg_attr` can contains multiple `Meta`s.
+ pub fn parent_attr(&self) -> Option<ast::Attr> {
+ self.syntax().ancestors().find_map(ast::Attr::cast)
+ }
+}
+
+impl ast::Attr {
+ pub fn as_simple_atom(&self) -> Option<SmolStr> {
+ self.meta().and_then(|meta| meta.as_simple_atom())
+ }
+
+ pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> {
+ self.meta().and_then(|meta| meta.as_simple_call())
+ }
+
+ pub fn as_simple_path(&self) -> Option<ast::Path> {
+ self.meta().and_then(|meta| meta.as_simple_path())
+ }
+
+ pub fn simple_name(&self) -> Option<SmolStr> {
+ self.meta().and_then(|meta| meta.simple_name())
+ }
+
pub fn path(&self) -> Option<ast::Path> {
- self.meta()?.path()
+ self.meta().and_then(|meta| meta.path())
}
- pub fn expr(&self) -> Option<ast::Expr> {
- self.meta()?.expr()
+ pub fn kind(&self) -> AttrKind {
+ match self.excl_token() {
+ Some(_) => AttrKind::Inner,
+ None => AttrKind::Outer,
+ }
}
- pub fn token_tree(&self) -> Option<ast::TokenTree> {
- self.meta()?.token_tree()
+ /// Includes `cfg_attr()` inner metas (without considering the predicate).
+ pub fn skip_cfg_attrs(&self) -> SmallVec<[ast::Meta; 1]> {
+ match self.meta() {
+ Some(meta) => meta.skip_cfg_attrs(),
+ None => SmallVec::new(),
+ }
}
}
@@ -1015,12 +1058,6 @@ impl ast::TokenTree {
}
}
-impl ast::Meta {
- pub fn parent_attr(&self) -> Option<ast::Attr> {
- self.syntax().parent().and_then(ast::Attr::cast)
- }
-}
-
impl ast::GenericArgList {
pub fn lifetime_args(&self) -> impl Iterator<Item = ast::LifetimeArg> {
self.generic_args().filter_map(|arg| match arg {
@@ -1164,6 +1201,25 @@ impl ast::OrPat {
}
}
+#[derive(Debug, Clone)]
+pub enum CfgAtomKey {
+ True,
+ False,
+ Ident(SyntaxToken),
+}
+
+impl ast::CfgAtom {
+ pub fn key(&self) -> Option<CfgAtomKey> {
+ if self.true_token().is_some() {
+ Some(CfgAtomKey::True)
+ } else if self.false_token().is_some() {
+ Some(CfgAtomKey::False)
+ } else {
+ self.ident_token().map(CfgAtomKey::Ident)
+ }
+ }
+}
+
/// An iterator over the elements in an [`ast::TokenTree`].
///
/// Does not yield trivia or the delimiters.
diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs
index e91e444a32..c66f096e83 100644
--- a/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -1848,7 +1848,36 @@ impl SyntaxFactory {
if let Some(mut mapping) = self.mappings() {
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone());
- builder.map_node(tt.syntax().clone(), ast.token_tree().unwrap().syntax().clone());
+ let ast::Meta::TokenTreeMeta(meta) = &ast else { unreachable!() };
+ builder.map_node(tt.syntax().clone(), meta.token_tree().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn cfg_flag(&self, flag: &str) -> ast::CfgPredicate {
+ make::cfg_flag(flag).clone_for_update()
+ }
+
+ pub fn cfg_attr_meta(
+ &self,
+ predicate: ast::CfgPredicate,
+ inner: impl IntoIterator<Item = ast::Meta>,
+ ) -> ast::CfgAttrMeta {
+ let inner = Vec::from_iter(inner);
+ let ast = make::cfg_attr_meta(predicate.clone(), inner.iter().cloned()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(
+ predicate.syntax().clone(),
+ ast.cfg_predicate().unwrap().syntax().clone(),
+ );
+ builder.map_children(
+ inner.iter().map(|it| it.syntax().clone()),
+ ast.metas().map(|it| it.syntax().clone()),
+ );
builder.finish(&mut mapping);
}
diff --git a/crates/syntax/src/ast/traits.rs b/crates/syntax/src/ast/traits.rs
index 2f4109a2c9..6fe5abb84e 100644
--- a/crates/syntax/src/ast/traits.rs
+++ b/crates/syntax/src/ast/traits.rs
@@ -73,9 +73,6 @@ pub trait HasAttrs: AstNode {
fn attrs(&self) -> AstChildren<ast::Attr> {
support::children(self.syntax())
}
- fn has_atom_attr(&self, atom: &str) -> bool {
- self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom)
- }
/// This may return the same node as called with (with `SourceFile`). The caller has the responsibility
/// to avoid duplicate attributes.
diff --git a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
index 50057a02d8..5fdde93c60 100644
--- a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
+++ b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
@@ -29,7 +29,7 @@ [email protected]
@@ -60,7 +60,7 @@ [email protected]
@@ -75,7 +75,7 @@ [email protected]
@@ -104,7 +104,7 @@ [email protected]
diff --git a/xtask/src/codegen/grammar.rs b/xtask/src/codegen/grammar.rs
index 4e980bb3d9..257429c426 100644
--- a/xtask/src/codegen/grammar.rs
+++ b/xtask/src/codegen/grammar.rs
@@ -780,7 +780,7 @@ impl Field {
}
fn token_kind(&self) -> Option<proc_macro2::TokenStream> {
match self {
- Field::Token(token) => {
+ Field::Token { token, .. } => {
let token: proc_macro2::TokenStream = token.parse().unwrap();
Some(quote! { T![#token] })
}
@@ -789,8 +789,11 @@ impl Field {
}
fn method_name(&self) -> String {
match self {
- Field::Token(name) => {
- let name = match name.as_str() {
+ Field::Token { name, token, .. } => {
+ if let Some(name) = name {
+ return name.clone();
+ }
+ let name = match token.as_str() {
";" => "semicolon",
"->" => "thin_arrow",
"'{'" => "l_curly",
@@ -820,7 +823,7 @@ impl Field {
"," => "comma",
"|" => "pipe",
"~" => "tilde",
- _ => name,
+ _ => token,
};
format!("{name}_token",)
}
@@ -835,7 +838,7 @@ impl Field {
}
fn ty(&self) -> proc_macro2::Ident {
match self {
- Field::Token(_) => format_ident!("SyntaxToken"),
+ Field::Token { .. } => format_ident!("SyntaxToken"),
Field::Node { ty, .. } => format_ident!("{}", ty),
}
}
@@ -885,7 +888,7 @@ fn lower(grammar: &Grammar) -> AstSrc {
res.nodes.iter_mut().for_each(|it| {
it.traits.sort();
it.fields.sort_by_key(|it| match it {
- Field::Token(name) => (true, name.clone()),
+ Field::Token { token, .. } => (true, token.clone()),
Field::Node { name, .. } => (false, name.clone()),
});
});
@@ -925,12 +928,11 @@ fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, r
acc.push(field);
}
Rule::Token(token) => {
- assert!(label.is_none());
- let mut name = clean_token_name(&grammar[*token].name);
- if "[]{}()".contains(&name) {
- name = format!("'{name}'");
+ let mut token = clean_token_name(&grammar[*token].name);
+ if "[]{}()".contains(&token) {
+ token = format!("'{token}'");
}
- let field = Field::Token(name);
+ let field = Field::Token { name: label.cloned(), token };
acc.push(field);
}
Rule::Rep(inner) => {
@@ -1018,8 +1020,8 @@ fn lower_separated_list(
}
match nt {
Either::Right(token) => {
- let name = clean_token_name(&grammar[*token].name);
- let field = Field::Token(name);
+ let token = clean_token_name(&grammar[*token].name);
+ let field = Field::Token { token, name: None };
acc.push(field);
}
Either::Left(node) => {
diff --git a/xtask/src/codegen/grammar/ast_src.rs b/xtask/src/codegen/grammar/ast_src.rs
index 564d9cc24e..a0abdf09d3 100644
--- a/xtask/src/codegen/grammar/ast_src.rs
+++ b/xtask/src/codegen/grammar/ast_src.rs
@@ -111,8 +111,19 @@ const RESERVED: &[&str] = &[
];
// keywords that are keywords only in specific parse contexts
#[doc(alias = "WEAK_KEYWORDS")]
-const CONTEXTUAL_KEYWORDS: &[&str] =
- &["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet", "safe", "bikeshed"];
+const CONTEXTUAL_KEYWORDS: &[&str] = &[
+ "macro_rules",
+ "union",
+ "default",
+ "raw",
+ "dyn",
+ "auto",
+ "yeet",
+ "safe",
+ "bikeshed",
+ "cfg_attr",
+ "cfg",
+];
// keywords we use for special macro expansions
const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[
"asm",
@@ -261,7 +272,7 @@ pub(crate) struct AstNodeSrc {
#[derive(Debug, Eq, PartialEq)]
pub(crate) enum Field {
- Token(String),
+ Token { name: Option<String>, token: String },
Node { name: String, ty: String, cardinality: Cardinality },
}