Unnamed repository; edit this file 'description' to name the repository.
Implement unsafe attribute parsing
Lukas Wirth 2024-05-06
parent c4618fe · commit e1aeed3
-rw-r--r--crates/hir-expand/src/attrs.rs16
-rw-r--r--crates/ide/src/runnables.rs14
-rw-r--r--crates/parser/src/grammar/attributes.rs28
-rw-r--r--crates/parser/test_data/parser/inline/ok/0213_metas.rast457
-rw-r--r--crates/parser/test_data/parser/inline/ok/0213_metas.rs20
-rw-r--r--crates/syntax/rust.ungram5
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs3
-rw-r--r--crates/syntax/src/validation/block.rs2
8 files changed, 534 insertions, 11 deletions
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index feffcb7ce5..85ec02ae07 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -252,8 +252,18 @@ impl Attr {
Some(Attr { id, path, input, ctxt: span.ctx })
}
- fn from_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree], id: AttrId) -> Option<Attr> {
- let ctxt = tt.first()?.first_span().ctx;
+ fn from_tt(db: &dyn ExpandDatabase, mut tt: &[tt::TokenTree], id: AttrId) -> Option<Attr> {
+ if matches!(tt,
+ [tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text, .. })), ..]
+ if text == "unsafe"
+ ) {
+ match tt.get(1) {
+ Some(tt::TokenTree::Subtree(subtree)) => tt = &subtree.token_trees,
+ _ => return None,
+ }
+ }
+ let first = &tt.first()?;
+ let ctxt = first.first_span().ctx;
let path_end = tt
.iter()
.position(|tt| {
@@ -435,7 +445,7 @@ fn inner_attributes(
// Input subtree is: `(cfg, $(attr),+)`
// Split it up into a `cfg` subtree and the `attr` subtrees.
-pub fn parse_cfg_attr_input(
+fn parse_cfg_attr_input(
subtree: &Subtree,
) -> Option<(&[tt::TokenTree], impl Iterator<Item = &[tt::TokenTree]>)> {
let mut parts = subtree
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index b6c6753755..20e998846b 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -609,6 +609,9 @@ fn main() {}
#[export_name = "main"]
fn __cortex_m_rt_main_trampoline() {}
+#[unsafe(export_name = "main")]
+fn __cortex_m_rt_main_trampoline_unsafe() {}
+
#[test]
fn test_foo() {}
@@ -628,13 +631,14 @@ mod not_a_root {
"#,
expect![[r#"
[
- "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..253, name: \"\", kind: Module })",
+ "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..331, name: \"\", kind: Module })",
"(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })",
"(Bin, NavigationTarget { file_id: FileId(0), full_range: 15..76, focus_range: 42..71, name: \"__cortex_m_rt_main_trampoline\", kind: Function })",
- "(Test, NavigationTarget { file_id: FileId(0), full_range: 78..102, focus_range: 89..97, name: \"test_foo\", kind: Function })",
- "(Test, NavigationTarget { file_id: FileId(0), full_range: 104..155, focus_range: 136..150, name: \"test_full_path\", kind: Function })",
- "(Test, NavigationTarget { file_id: FileId(0), full_range: 157..191, focus_range: 178..186, name: \"test_foo\", kind: Function })",
- "(Bench, NavigationTarget { file_id: FileId(0), full_range: 193..215, focus_range: 205..210, name: \"bench\", kind: Function })",
+ "(Bin, NavigationTarget { file_id: FileId(0), full_range: 78..154, focus_range: 113..149, name: \"__cortex_m_rt_main_trampoline_unsafe\", kind: Function })",
+ "(Test, NavigationTarget { file_id: FileId(0), full_range: 156..180, focus_range: 167..175, name: \"test_foo\", kind: Function })",
+ "(Test, NavigationTarget { file_id: FileId(0), full_range: 182..233, focus_range: 214..228, name: \"test_full_path\", kind: Function })",
+ "(Test, NavigationTarget { file_id: FileId(0), full_range: 235..269, focus_range: 256..264, name: \"test_foo\", kind: Function })",
+ "(Bench, NavigationTarget { file_id: FileId(0), full_range: 271..293, focus_range: 283..288, name: \"bench\", kind: Function })",
]
"#]],
);
diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs
index c13a194379..82e4d66148 100644
--- a/crates/parser/src/grammar/attributes.rs
+++ b/crates/parser/src/grammar/attributes.rs
@@ -36,8 +36,33 @@ fn attr(p: &mut Parser<'_>, inner: bool) {
attr.complete(p, ATTR);
}
+// test metas
+// #![simple_ident]
+// #![simple::path]
+// #![simple_ident_expr = ""]
+// #![simple::path::Expr = ""]
+// #![simple_ident_tt(a b c)]
+// #![simple_ident_tt[a b c]]
+// #![simple_ident_tt{a b c}]
+// #![simple::path::tt(a b c)]
+// #![simple::path::tt[a b c]]
+// #![simple::path::tt{a b c}]
+// #![unsafe(simple_ident)]
+// #![unsafe(simple::path)]
+// #![unsafe(simple_ident_expr = "")]
+// #![unsafe(simple::path::Expr = "")]
+// #![unsafe(simple_ident_tt(a b c))]
+// #![unsafe(simple_ident_tt[a b c])]
+// #![unsafe(simple_ident_tt{a b c})]
+// #![unsafe(simple::path::tt(a b c))]
+// #![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 {
+ p.expect(T!['(']);
+ }
paths::use_path(p);
match p.current() {
@@ -50,6 +75,9 @@ pub(super) fn meta(p: &mut Parser<'_>) {
T!['('] | T!['['] | T!['{'] => items::token_tree(p),
_ => {}
}
+ if is_unsafe {
+ p.expect(T![')']);
+ }
meta.complete(p, META);
}
diff --git a/crates/parser/test_data/parser/inline/ok/0213_metas.rast b/crates/parser/test_data/parser/inline/ok/0213_metas.rast
new file mode 100644
index 0000000000..b1ac60b530
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/0213_metas.rast
@@ -0,0 +1,457 @@
+SOURCE_FILE
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple_ident"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ COLON2 "::"
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "path"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple_ident_expr"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ STRING "\"\""
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ PATH
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ COLON2 "::"
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "path"
+ COLON2 "::"
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Expr"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ STRING "\"\""
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ 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_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ 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_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ 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_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ PATH
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ 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 ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ PATH
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ 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 "]"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ PATH
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ 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 "}"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ UNSAFE_KW "unsafe"
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple_ident"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ UNSAFE_KW "unsafe"
+ L_PAREN "("
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ COLON2 "::"
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "path"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ UNSAFE_KW "unsafe"
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple_ident_expr"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ STRING "\"\""
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ UNSAFE_KW "unsafe"
+ L_PAREN "("
+ PATH
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ COLON2 "::"
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "path"
+ COLON2 "::"
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Expr"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ STRING "\"\""
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ 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 ")"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ 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 "]"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ 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 "}"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ UNSAFE_KW "unsafe"
+ L_PAREN "("
+ PATH
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ 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 ")"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ UNSAFE_KW "unsafe"
+ L_PAREN "("
+ PATH
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ 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 "]"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ UNSAFE_KW "unsafe"
+ L_PAREN "("
+ PATH
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ 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 "}"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0213_metas.rs b/crates/parser/test_data/parser/inline/ok/0213_metas.rs
new file mode 100644
index 0000000000..57b7bb7170
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/0213_metas.rs
@@ -0,0 +1,20 @@
+#![simple_ident]
+#![simple::path]
+#![simple_ident_expr = ""]
+#![simple::path::Expr = ""]
+#![simple_ident_tt(a b c)]
+#![simple_ident_tt[a b c]]
+#![simple_ident_tt{a b c}]
+#![simple::path::tt(a b c)]
+#![simple::path::tt[a b c]]
+#![simple::path::tt{a b c}]
+#![unsafe(simple_ident)]
+#![unsafe(simple::path)]
+#![unsafe(simple_ident_expr = "")]
+#![unsafe(simple::path::Expr = "")]
+#![unsafe(simple_ident_tt(a b c))]
+#![unsafe(simple_ident_tt[a b c])]
+#![unsafe(simple_ident_tt{a b c})]
+#![unsafe(simple::path::tt(a b c))]
+#![unsafe(simple::path::tt[a b c])]
+#![unsafe(simple::path::tt{a b c})]
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 8a775dd874..8c772b9c7a 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -314,7 +314,9 @@ Attr =
'#' '!'? '[' Meta ']'
Meta =
- Path ('=' Expr | TokenTree)?
+ 'unsafe' '(' Path ('=' Expr | TokenTree)? ')'
+| Path ('=' Expr | TokenTree)?
+
//****************************//
// Statements and Expressions //
@@ -391,7 +393,6 @@ FormatArgsExpr =
FormatArgsArg =
(Name '=')? Expr
-# MacroCallExpr
MacroExpr =
MacroCall
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index c82bc4151a..98186c5473 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -778,7 +778,10 @@ impl Meta {
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
pub fn token_tree(&self) -> Option<TokenTree> { support::child(&self.syntax) }
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+ pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/syntax/src/validation/block.rs b/crates/syntax/src/validation/block.rs
index 8eb4a10a3f..fe3d61bef1 100644
--- a/crates/syntax/src/validation/block.rs
+++ b/crates/syntax/src/validation/block.rs
@@ -9,7 +9,7 @@ use crate::{
pub(crate) fn validate_block_expr(block: ast::BlockExpr, errors: &mut Vec<SyntaxError>) {
if let Some(parent) = block.syntax().parent() {
match parent.kind() {
- FN | EXPR_STMT | STMT_LIST => return,
+ FN | EXPR_STMT | STMT_LIST | MACRO_STMTS => return,
_ => {}
}
}