diff --git a/crates/hir/src/hir_def/expr.rs b/crates/hir/src/hir_def/expr.rs index 18ec5596c..66ec65089 100644 --- a/crates/hir/src/hir_def/expr.rs +++ b/crates/hir/src/hir_def/expr.rs @@ -40,6 +40,8 @@ pub enum Expr { /// The first `ExprId` is the scrutinee, the second is the arms. Match(ExprId, Partial>), + Unsafe(ExprId), + /// The `Assign` Expression. The first `ExprId` is the destination of the assignment, /// and the second `ExprId` is the rhs value of the binding. Assign(ExprId, ExprId), diff --git a/crates/hir/src/lower/expr.rs b/crates/hir/src/lower/expr.rs index 54f192196..a2ed034a7 100644 --- a/crates/hir/src/lower/expr.rs +++ b/crates/hir/src/lower/expr.rs @@ -172,6 +172,11 @@ impl Expr { Self::Match(scrutinee, arm) } + ast::ExprKind::Unsafe(unsafe_expr) => { + let inner = Self::push_to_body_opt(ctxt, unsafe_expr.inner_expr()); + Self::Unsafe(inner) + } + ast::ExprKind::Paren(paren) => { return Self::push_to_body_opt(ctxt, paren.expr()); } diff --git a/crates/hir/src/span/expr.rs b/crates/hir/src/span/expr.rs index 2e0ab24f8..cec0f4983 100644 --- a/crates/hir/src/span/expr.rs +++ b/crates/hir/src/span/expr.rs @@ -199,6 +199,14 @@ define_lazy_span_node!( define_lazy_span_node!(LazyMatchArmSpan); +define_lazy_span_node!( + LazyUnsafeExprSpan, + ast::UnsafeExpr, + @token { + (unsafe_kw, unsafe_kw), + } +); + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub(crate) struct ExprRoot { expr: ExprId, diff --git a/crates/hir/src/visitor.rs b/crates/hir/src/visitor.rs index 60a32d887..8095019d5 100644 --- a/crates/hir/src/visitor.rs +++ b/crates/hir/src/visitor.rs @@ -1087,6 +1087,10 @@ where } } + Expr::Unsafe(expr_id) => { + visit_node_in_body!(visitor, ctxt, expr_id, expr); + } + Expr::Assign(left_expr_id, right_expr_id) => { visit_node_in_body!(visitor, ctxt, left_expr_id, expr); visit_node_in_body!(visitor, ctxt, right_expr_id, expr); diff --git a/crates/parser2/src/ast/expr.rs b/crates/parser2/src/ast/expr.rs index e3a1a8dde..fcf4ace4f 100644 --- a/crates/parser2/src/ast/expr.rs +++ b/crates/parser2/src/ast/expr.rs @@ -22,6 +22,7 @@ ast_node! { | SK::LitExpr | SK::IfExpr | SK::MatchExpr + | SK::UnsafeExpr | SK::ParenExpr | SK::AssignExpr | SK::AugAssignExpr, @@ -50,6 +51,7 @@ impl Expr { SK::LitExpr => ExprKind::Lit(AstNode::cast(self.syntax().clone()).unwrap()), SK::IfExpr => ExprKind::If(AstNode::cast(self.syntax().clone()).unwrap()), SK::MatchExpr => ExprKind::Match(AstNode::cast(self.syntax().clone()).unwrap()), + SK::UnsafeExpr => ExprKind::Unsafe(AstNode::cast(self.syntax().clone()).unwrap()), SK::ParenExpr => ExprKind::Paren(AstNode::cast(self.syntax().clone()).unwrap()), SK::AssignExpr => ExprKind::Assign(AstNode::cast(self.syntax().clone()).unwrap()), SK::AugAssignExpr => ExprKind::AugAssign(AstNode::cast(self.syntax().clone()).unwrap()), @@ -326,6 +328,21 @@ impl MatchExpr { } } +ast_node! { + /// `unsafe expr` + pub struct UnsafeExpr, + SK::UnsafeExpr +} +impl UnsafeExpr { + pub fn unsafe_kw(&self) -> Option { + support::token(self.syntax(), SK::UnsafeKw) + } + + pub fn inner_expr(&self) -> Option { + support::child(self.syntax()) + } +} + ast_node! { /// `(expr)` pub struct ParenExpr, @@ -398,6 +415,7 @@ pub enum ExprKind { ArrayRep(ArrayRepExpr), If(IfExpr), Match(MatchExpr), + Unsafe(UnsafeExpr), Paren(ParenExpr), Assign(AssignExpr), AugAssign(AugAssignExpr), diff --git a/crates/parser2/src/parser/expr_atom.rs b/crates/parser2/src/parser/expr_atom.rs index 08578e05b..f5eb4eefd 100644 --- a/crates/parser2/src/parser/expr_atom.rs +++ b/crates/parser2/src/parser/expr_atom.rs @@ -22,7 +22,7 @@ use super::{ pub(super) fn is_expr_atom_head(kind: SyntaxKind) -> bool { use SyntaxKind::*; match kind { - IfKw | MatchKw | LBrace | LParen | LBracket => true, + IfKw | MatchKw | LBrace | LParen | LBracket | UnsafeKw => true, kind if lit::is_lit(kind) => true, kind if path::is_path_segment(kind) => true, _ => false, @@ -38,6 +38,7 @@ pub(super) fn parse_expr_atom( match parser.current_kind() { Some(IfKw) => parser.parse_cp(IfExprScope::default(), None), Some(MatchKw) => parser.parse_cp(MatchExprScope::default(), None), + Some(UnsafeKw) => parser.parse_cp(UnsafeExprScope::default(), None), Some(LBrace) => parser.parse_cp(BlockExprScope::default(), None), Some(LParen) => parser.parse_cp(ParenScope::default(), None), Some(LBracket) => parser.parse_cp(ArrayScope::default(), None), @@ -83,8 +84,22 @@ impl super::Parse for BlockExprScope { .map(SyntaxKind::is_item_head) .unwrap_or_default() { - parser.parse(ItemScope::default())?; - continue; + let is_item = parser.dry_run(|parser| { + if parser.bump_if(SyntaxKind::UnsafeKw) { + parser + .current_kind() + .map(SyntaxKind::is_item_head) + .unwrap_or_default() + && !parser.bump_if(SyntaxKind::UnsafeKw) + } else { + true + } + }); + + if is_item { + parser.parse(ItemScope::default())?; + continue; + } } parse_stmt(parser)?; @@ -131,6 +146,17 @@ impl super::Parse for IfExprScope { } } +define_scope! { UnsafeExprScope, UnsafeExpr } +impl super::Parse for UnsafeExprScope { + type Error = Recovery; + + fn parse(&mut self, parser: &mut Parser) -> Result<(), Self::Error> { + parser.bump_expected(SyntaxKind::UnsafeKw); + + parse_expr(parser) + } +} + define_scope! { MatchExprScope, MatchExpr } impl super::Parse for MatchExprScope { type Error = Recovery; diff --git a/crates/parser2/src/syntax_kind.rs b/crates/parser2/src/syntax_kind.rs index a1e9bb16b..8b0855740 100644 --- a/crates/parser2/src/syntax_kind.rs +++ b/crates/parser2/src/syntax_kind.rs @@ -282,6 +282,8 @@ pub enum SyntaxKind { LitExpr, /// `if x { 1 } else { 2 }` IfExpr, + /// `unsafe foo(x)` + UnsafeExpr, /// `match x { pat => { .. } }` MatchExpr, /// `(1 + 2)` @@ -632,6 +634,7 @@ impl SyntaxKind { SyntaxKind::ArrayRepExpr => "array expression", SyntaxKind::LitExpr => "literal expression", SyntaxKind::IfExpr => "`if` expression", + SyntaxKind::UnsafeExpr => "`unsafe` expression", SyntaxKind::MatchExpr => "`match` expression", SyntaxKind::ParenExpr => "parenthesized expression", SyntaxKind::AssignExpr => "assignment expression", diff --git a/crates/parser2/test_files/syntax_node/exprs/unsafe.fe b/crates/parser2/test_files/syntax_node/exprs/unsafe.fe new file mode 100644 index 000000000..58f69844b --- /dev/null +++ b/crates/parser2/test_files/syntax_node/exprs/unsafe.fe @@ -0,0 +1,11 @@ +unsafe { foo() } + +unsafe { + let a = 42 + std::evm::mstore(offset: 0, value: a) + return +} + +unsafe 1 + 1 + +unsafe bar() diff --git a/crates/parser2/test_files/syntax_node/exprs/unsafe.snap b/crates/parser2/test_files/syntax_node/exprs/unsafe.snap new file mode 100644 index 000000000..b47a242ea --- /dev/null +++ b/crates/parser2/test_files/syntax_node/exprs/unsafe.snap @@ -0,0 +1,112 @@ +--- +source: crates/parser2/tests/syntax_node.rs +expression: node +input_file: crates/parser2/test_files/syntax_node/exprs/unsafe.fe +--- +Root@0..125 + UnsafeExpr@0..16 + UnsafeKw@0..6 "unsafe" + WhiteSpace@6..7 " " + BlockExpr@7..16 + LBrace@7..8 "{" + WhiteSpace@8..9 " " + ExprStmt@9..14 + CallExpr@9..14 + PathExpr@9..12 + Path@9..12 + PathSegment@9..12 + Ident@9..12 "foo" + CallArgList@12..14 + LParen@12..13 "(" + RParen@13..14 ")" + WhiteSpace@14..15 " " + RBrace@15..16 "}" + Newline@16..18 "\n\n" + UnsafeExpr@18..96 + UnsafeKw@18..24 "unsafe" + WhiteSpace@24..25 " " + BlockExpr@25..96 + LBrace@25..26 "{" + Newline@26..27 "\n" + WhiteSpace@27..31 " " + LetStmt@31..41 + LetKw@31..34 "let" + WhiteSpace@34..35 " " + PathPat@35..36 + Path@35..36 + PathSegment@35..36 + Ident@35..36 "a" + WhiteSpace@36..37 " " + Eq@37..38 "=" + WhiteSpace@38..39 " " + LitExpr@39..41 + Lit@39..41 + Int@39..41 "42" + Newline@41..42 "\n" + WhiteSpace@42..46 " " + ExprStmt@46..83 + CallExpr@46..83 + PathExpr@46..62 + Path@46..62 + PathSegment@46..49 + Ident@46..49 "std" + Colon2@49..51 "::" + PathSegment@51..54 + Ident@51..54 "evm" + Colon2@54..56 "::" + PathSegment@56..62 + Ident@56..62 "mstore" + CallArgList@62..83 + LParen@62..63 "(" + CallArg@63..72 + Ident@63..69 "offset" + Colon@69..70 ":" + WhiteSpace@70..71 " " + LitExpr@71..72 + Lit@71..72 + Int@71..72 "0" + Comma@72..73 "," + WhiteSpace@73..74 " " + CallArg@74..82 + Ident@74..79 "value" + Colon@79..80 ":" + WhiteSpace@80..81 " " + PathExpr@81..82 + Path@81..82 + PathSegment@81..82 + Ident@81..82 "a" + RParen@82..83 ")" + Newline@83..84 "\n" + WhiteSpace@84..88 " " + ReturnStmt@88..94 + ReturnKw@88..94 "return" + Newline@94..95 "\n" + RBrace@95..96 "}" + Newline@96..98 "\n\n" + UnsafeExpr@98..110 + UnsafeKw@98..104 "unsafe" + WhiteSpace@104..105 " " + BinExpr@105..110 + LitExpr@105..106 + Lit@105..106 + Int@105..106 "1" + WhiteSpace@106..107 " " + Plus@107..108 "+" + WhiteSpace@108..109 " " + LitExpr@109..110 + Lit@109..110 + Int@109..110 "1" + Newline@110..112 "\n\n" + UnsafeExpr@112..124 + UnsafeKw@112..118 "unsafe" + WhiteSpace@118..119 " " + CallExpr@119..124 + PathExpr@119..122 + Path@119..122 + PathSegment@119..122 + Ident@119..122 "bar" + CallArgList@122..124 + LParen@122..123 "(" + RParen@123..124 ")" + Newline@124..125 "\n" + diff --git a/crates/parser2/test_files/syntax_node/stmts/let.fe b/crates/parser2/test_files/syntax_node/stmts/let.fe index 4582f9b69..82cb2974c 100644 --- a/crates/parser2/test_files/syntax_node/stmts/let.fe +++ b/crates/parser2/test_files/syntax_node/stmts/let.fe @@ -20,4 +20,6 @@ let x = if b { let x = match b { MyEnum::A(x) | MyEnum::B(x) => x _ => 0 -} \ No newline at end of file +} + +let x = unsafe bar() \ No newline at end of file diff --git a/crates/parser2/test_files/syntax_node/stmts/let.snap b/crates/parser2/test_files/syntax_node/stmts/let.snap index ffd3cf7c1..07bc3e6fa 100644 --- a/crates/parser2/test_files/syntax_node/stmts/let.snap +++ b/crates/parser2/test_files/syntax_node/stmts/let.snap @@ -3,7 +3,7 @@ source: crates/parser2/tests/syntax_node.rs expression: node input_file: crates/parser2/test_files/syntax_node/stmts/let.fe --- -Root@0..231 +Root@0..253 LetStmt@0..5 LetKw@0..3 "let" WhiteSpace@3..4 " " @@ -298,4 +298,26 @@ Root@0..231 Int@228..229 "0" Newline@229..230 "\n" RBrace@230..231 "}" + Newline@231..233 "\n\n" + LetStmt@233..253 + LetKw@233..236 "let" + WhiteSpace@236..237 " " + PathPat@237..238 + Path@237..238 + PathSegment@237..238 + Ident@237..238 "x" + WhiteSpace@238..239 " " + Eq@239..240 "=" + WhiteSpace@240..241 " " + UnsafeExpr@241..253 + UnsafeKw@241..247 "unsafe" + WhiteSpace@247..248 " " + CallExpr@248..253 + PathExpr@248..251 + Path@248..251 + PathSegment@248..251 + Ident@248..251 "bar" + CallArgList@251..253 + LParen@251..252 "(" + RParen@252..253 ")"