From d846afdeef167679b89e14bb420826f3496df000 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 2 Jan 2022 18:41:32 +0300 Subject: [PATCH] check top level entry point invariants --- .../mbe/tt_conversion.rs | 6 ++-- crates/ide/src/hover/tests.rs | 2 +- crates/mbe/src/syntax_bridge.rs | 5 ++-- crates/parser/src/grammar.rs | 13 +++++++++ crates/parser/src/lib.rs | 28 +++++++++++++++---- 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs b/crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs index 4c58ea9ba6..5f4b7d6d0b 100644 --- a/crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs +++ b/crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs @@ -105,21 +105,21 @@ macro_rules! m2 { ($x:ident) => {} } #[test] fn expansion_does_not_parse_as_expression() { - cov_mark::check!(expansion_does_not_parse_as_expression); check( r#" macro_rules! stmts { () => { let _ = 0; } } -fn f() { let _ = stmts!(); } +fn f() { let _ = stmts!/*+errors*/(); } "#, expect![[r#" macro_rules! stmts { () => { let _ = 0; } } -fn f() { let _ = /* error: could not convert tokens */; } +fn f() { let _ = /* parse error: expected expression */ +let _ = 0;; } "#]], ) } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 82fc385040..2bfda1aff2 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -1148,7 +1148,7 @@ fn foo() { let a = id!([0u32, bar($0)] ); } fn test_hover_through_literal_string_in_macro() { check( r#" -macro_rules! arr { ($($tt:tt)*) => { [$($tt)*)] } } +macro_rules! arr { ($($tt:tt)*) => { [$($tt)*] } } fn foo() { let mastered_for_itunes = ""; let _ = arr!("Tr$0acks", &mastered_for_itunes); diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index 1141365e82..7d7807206f 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs @@ -1,7 +1,7 @@ //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`]. use rustc_hash::{FxHashMap, FxHashSet}; -use stdx::non_empty_vec::NonEmptyVec; +use stdx::{never, non_empty_vec::NonEmptyVec}; use syntax::{ ast::{self, make::tokens::doc_comment}, AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement, SyntaxKind, @@ -66,8 +66,7 @@ pub fn token_tree_to_syntax_node( parser::Step::Error { msg } => tree_sink.error(msg.to_string()), } } - if tree_sink.roots.len() != 1 { - cov_mark::hit!(expansion_does_not_parse_as_expression); + if never!(tree_sink.roots.len() != 1) { return Err(ExpandError::ConversionError); } //FIXME: would be cool to report errors diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index c6c111c9a8..0240a6f14f 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs @@ -148,6 +148,19 @@ pub(crate) mod entry { } m.complete(p, ERROR); } + + pub(crate) fn meta_item(p: &mut Parser) { + let m = p.start(); + attributes::meta(p); + if p.at(EOF) { + m.abandon(p); + return; + } + while !p.at(EOF) { + p.bump_any(); + } + m.complete(p, ERROR); + } } } diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 491a657a34..9723a17c64 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -99,9 +99,11 @@ impl PrefixEntryPoint { /// ``` /// /// the input to the macro will be parsed with [`PrefixEntryPoint::Item`], and -/// the result will be [`TopEntryPoint::Items`]. +/// the result will be [`TopEntryPoint::MacroItems`]. /// -/// This *should* (but currently doesn't) guarantee that all input is consumed. +/// [`TopEntryPoint::parse`] makes a guarantee that +/// * all input is consumed +/// * the result is a valid tree (there's one root node) #[derive(Debug)] pub enum TopEntryPoint { SourceFile, @@ -124,13 +126,29 @@ impl TopEntryPoint { TopEntryPoint::Pattern => grammar::entry::top::pattern, TopEntryPoint::Type => grammar::entry::top::type_, TopEntryPoint::Expr => grammar::entry::top::expr, - // FIXME - TopEntryPoint::MetaItem => grammar::entry::prefix::meta_item, + TopEntryPoint::MetaItem => grammar::entry::top::meta_item, }; let mut p = parser::Parser::new(input); entry_point(&mut p); let events = p.finish(); - event::process(events) + let res = event::process(events); + + if cfg!(debug_assertions) { + let mut depth = 0; + let mut first = true; + for step in res.iter() { + assert!(depth > 0 || first); + first = false; + match step { + Step::Enter { .. } => depth += 1, + Step::Exit => depth -= 1, + Step::Token { .. } | Step::Error { .. } => (), + } + } + assert!(!first, "no tree at all"); + } + + res } }