diff --git a/peg-macros/grammar.rs b/peg-macros/grammar.rs index ae8f75e..abea07c 100644 --- a/peg-macros/grammar.rs +++ b/peg-macros/grammar.rs @@ -20,6 +20,7 @@ pub mod peg { use crate::ast::Expr::*; use crate::ast::*; use crate::tokens::FlatTokenStream; + use crate::translate::report_error_expr; use proc_macro2::{Delimiter, Group, Ident, Literal, Span, TokenStream}; pub fn peg_grammar<'input>( __input: &'input Input, @@ -468,7 +469,7 @@ pub mod peg { header, ) => { let __seq_res = match match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , "->") { :: peg :: RuleResult :: Matched (__pos , __val) => { { let __seq_res = { let str_start = __pos ; match match __parse_rust_type (__input , __state , __err_state , __pos) { :: peg :: RuleResult :: Matched (pos , _) => :: peg :: RuleResult :: Matched (pos , ()) , :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } { :: peg :: RuleResult :: Matched (__newpos , _) => { :: peg :: RuleResult :: Matched (__newpos , :: peg :: ParseSlice :: parse_slice (__input , str_start , __newpos)) } , :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , t) => { :: peg :: RuleResult :: Matched (__pos , (|| { t }) ()) } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\"->\"") ; :: peg :: RuleResult :: Failed } } { :: peg :: RuleResult :: Matched (__newpos , __value) => { :: peg :: RuleResult :: Matched (__newpos , Some (__value)) } , :: peg :: RuleResult :: Failed => { :: peg :: RuleResult :: Matched (__pos , None) } , } ; - match __seq_res { :: peg :: RuleResult :: Matched (__pos , ret_type) => { { let __seq_res = match { let str_start = __pos ; match match __parse_rust_where_clause (__input , __state , __err_state , __pos) { :: peg :: RuleResult :: Matched (pos , _) => :: peg :: RuleResult :: Matched (pos , ()) , :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } { :: peg :: RuleResult :: Matched (__newpos , _) => { :: peg :: RuleResult :: Matched (__newpos , :: peg :: ParseSlice :: parse_slice (__input , str_start , __newpos)) } , :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } { :: peg :: RuleResult :: Matched (__newpos , __value) => { :: peg :: RuleResult :: Matched (__newpos , Some (__value)) } , :: peg :: RuleResult :: Failed => { :: peg :: RuleResult :: Matched (__pos , None) } , } ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , where_clause) => { match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , "=") { :: peg :: RuleResult :: Matched (__pos , __val) => { { let __seq_res = __parse_expression (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , expr) => { { let __seq_res = match match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , ";") { :: peg :: RuleResult :: Matched (__pos , __val) => { :: peg :: RuleResult :: Matched (__pos , __val) } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\";\"") ; :: peg :: RuleResult :: Failed } } { :: peg :: RuleResult :: Matched (__newpos , _) => { :: peg :: RuleResult :: Matched (__newpos , ()) } , :: peg :: RuleResult :: Failed => { :: peg :: RuleResult :: Matched (__pos , ()) } , } ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , _) => { :: peg :: RuleResult :: Matched (__pos , (|| { Rule { span , doc , name : header . 0 , ty_params : header . 1 , params : header . 2 , expr , ret_type , where_clause , visibility , no_eof , cache } }) ()) } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\"=\"") ; :: peg :: RuleResult :: Failed } } } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } + match __seq_res { :: peg :: RuleResult :: Matched (__pos , ret_type) => { { let __seq_res = match { let str_start = __pos ; match match __parse_rust_where_clause (__input , __state , __err_state , __pos) { :: peg :: RuleResult :: Matched (pos , _) => :: peg :: RuleResult :: Matched (pos , ()) , :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } { :: peg :: RuleResult :: Matched (__newpos , _) => { :: peg :: RuleResult :: Matched (__newpos , :: peg :: ParseSlice :: parse_slice (__input , str_start , __newpos)) } , :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } { :: peg :: RuleResult :: Matched (__newpos , __value) => { :: peg :: RuleResult :: Matched (__newpos , Some (__value)) } , :: peg :: RuleResult :: Failed => { :: peg :: RuleResult :: Matched (__pos , None) } , } ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , where_clause) => { { let __seq_res = match match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , "=") { :: peg :: RuleResult :: Matched (__pos , __val) => { { let __seq_res = __parse_expression (__input , __state , __err_state , __pos) ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , e) => { :: peg :: RuleResult :: Matched (__pos , (|| { e }) ()) } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\"=\"") ; :: peg :: RuleResult :: Failed } } { :: peg :: RuleResult :: Matched (__newpos , __value) => { :: peg :: RuleResult :: Matched (__newpos , Some (__value)) } , :: peg :: RuleResult :: Failed => { :: peg :: RuleResult :: Matched (__pos , None) } , } ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , expr) => { { let __seq_res = match match :: peg :: ParseLiteral :: parse_string_literal (__input , __pos , ";") { :: peg :: RuleResult :: Matched (__pos , __val) => { :: peg :: RuleResult :: Matched (__pos , __val) } :: peg :: RuleResult :: Failed => { __err_state . mark_failure (__pos , "\";\"") ; :: peg :: RuleResult :: Failed } } { :: peg :: RuleResult :: Matched (__newpos , _) => { :: peg :: RuleResult :: Matched (__newpos , ()) } , :: peg :: RuleResult :: Failed => { :: peg :: RuleResult :: Matched (__pos , ()) } , } ; match __seq_res { :: peg :: RuleResult :: Matched (__pos , _) => { :: peg :: RuleResult :: Matched (__pos , (|| { let expr = expr . unwrap_or_else (|| ActionExpr (vec ! [] , Some (Group :: new (Delimiter :: Brace , report_error_expr (span , "missing rule body" . to_owned ())))) . at (span)) ; Rule { span , doc , name : header . 0 , ty_params : header . 1 , params : header . 2 , expr , ret_type , where_clause , visibility , no_eof , cache } }) ()) } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } } :: peg :: RuleResult :: Failed => :: peg :: RuleResult :: Failed , } } ::peg::RuleResult::Failed => { ::peg::RuleResult::Failed diff --git a/peg-macros/grammar.rustpeg b/peg-macros/grammar.rustpeg index d05343f..a2814af 100644 --- a/peg-macros/grammar.rustpeg +++ b/peg-macros/grammar.rustpeg @@ -3,6 +3,7 @@ pub grammar peg() for FlatTokenStream { use crate::ast::*; use crate::ast::Expr::*; use crate::tokens::FlatTokenStream; +use crate::translate::report_error_expr; use proc_macro2::{ TokenStream, Ident, Group, Literal, Delimiter, Span }; pub rule peg_grammar() -> Grammar @@ -24,8 +25,11 @@ rule peg_rule() -> Rule ) ret_type:("->" t:$(rust_type()) {t})? where_clause:$(rust_where_clause())? - "=" expr:expression() ";"? - { Rule { span, doc, name:header.0, ty_params:header.1, params:header.2, expr, ret_type, where_clause, visibility, no_eof, cache } } + expr:("=" e:expression() {e})? ";"? { + // If expr is missing, allow macro expansion so rust-analyzer can complete the return type, but fail full compilation. + let expr = expr.unwrap_or_else(|| ActionExpr(vec![], Some(Group::new(Delimiter::Brace, report_error_expr(span, "missing rule body".to_owned())))).at(span)); + Rule { span, doc, name: header.0, ty_params: header.1, params:header.2, expr, ret_type, where_clause, visibility, no_eof, cache } + } rule cacheflag() -> Option = "#" "[" "cache" "]" {Some(Cache::Simple)} / "#" "[" "cache_left_rec" "]" {Some(Cache::Recursive)} / {None} diff --git a/tests/compile-fail/rule-missing-eq.rs b/tests/compile-fail/rule-missing-eq.rs new file mode 100644 index 0000000..3da15c7 --- /dev/null +++ b/tests/compile-fail/rule-missing-eq.rs @@ -0,0 +1,6 @@ +// We allow macro expansion to handle incomplete rules so rust-analyzer can complete a return type, but it should not compile +peg::parser!(grammar test() for str { + rule test() +}); + +fn main() {} diff --git a/tests/compile-fail/rule-missing-eq.stderr b/tests/compile-fail/rule-missing-eq.stderr new file mode 100644 index 0000000..49a5667 --- /dev/null +++ b/tests/compile-fail/rule-missing-eq.stderr @@ -0,0 +1,5 @@ +error: missing rule body + --> tests/compile-fail/rule-missing-eq.rs:3:5 + | +3 | rule test() + | ^^^^