Skip to content

Commit 6e3d135

Browse files
bors[bot]matklad
andauthored
Merge #11167
11167: internal: check top level entry point invariants r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
2 parents 68013ee + 2e34a5e commit 6e3d135

File tree

7 files changed

+177
-55
lines changed

7 files changed

+177
-55
lines changed

crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,21 @@ macro_rules! m2 { ($x:ident) => {} }
105105

106106
#[test]
107107
fn expansion_does_not_parse_as_expression() {
108-
cov_mark::check!(expansion_does_not_parse_as_expression);
109108
check(
110109
r#"
111110
macro_rules! stmts {
112111
() => { let _ = 0; }
113112
}
114113
115-
fn f() { let _ = stmts!(); }
114+
fn f() { let _ = stmts!/*+errors*/(); }
116115
"#,
117116
expect![[r#"
118117
macro_rules! stmts {
119118
() => { let _ = 0; }
120119
}
121120
122-
fn f() { let _ = /* error: could not convert tokens */; }
121+
fn f() { let _ = /* parse error: expected expression */
122+
let _ = 0;; }
123123
"#]],
124124
)
125125
}

crates/ide/src/hover/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1148,7 +1148,7 @@ fn foo() { let a = id!([0u32, bar($0)] ); }
11481148
fn test_hover_through_literal_string_in_macro() {
11491149
check(
11501150
r#"
1151-
macro_rules! arr { ($($tt:tt)*) => { [$($tt)*)] } }
1151+
macro_rules! arr { ($($tt:tt)*) => { [$($tt)*] } }
11521152
fn foo() {
11531153
let mastered_for_itunes = "";
11541154
let _ = arr!("Tr$0acks", &mastered_for_itunes);

crates/mbe/src/syntax_bridge.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Conversions between [`SyntaxNode`] and [`tt::TokenTree`].
22
33
use rustc_hash::{FxHashMap, FxHashSet};
4-
use stdx::non_empty_vec::NonEmptyVec;
4+
use stdx::{never, non_empty_vec::NonEmptyVec};
55
use syntax::{
66
ast::{self, make::tokens::doc_comment},
77
AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement, SyntaxKind,
@@ -66,8 +66,7 @@ pub fn token_tree_to_syntax_node(
6666
parser::Step::Error { msg } => tree_sink.error(msg.to_string()),
6767
}
6868
}
69-
if tree_sink.roots.len() != 1 {
70-
cov_mark::hit!(expansion_does_not_parse_as_expression);
69+
if never!(tree_sink.roots.len() != 1) {
7170
return Err(ExpandError::ConversionError);
7271
}
7372
//FIXME: would be cool to report errors

crates/parser/src/grammar.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,32 @@ pub(crate) mod entry {
135135
}
136136
m.complete(p, ERROR);
137137
}
138+
139+
pub(crate) fn expr(p: &mut Parser) {
140+
let m = p.start();
141+
expressions::expr(p);
142+
if p.at(EOF) {
143+
m.abandon(p);
144+
return;
145+
}
146+
while !p.at(EOF) {
147+
p.bump_any();
148+
}
149+
m.complete(p, ERROR);
150+
}
151+
152+
pub(crate) fn meta_item(p: &mut Parser) {
153+
let m = p.start();
154+
attributes::meta(p);
155+
if p.at(EOF) {
156+
m.abandon(p);
157+
return;
158+
}
159+
while !p.at(EOF) {
160+
p.bump_any();
161+
}
162+
m.complete(p, ERROR);
163+
}
138164
}
139165
}
140166

crates/parser/src/lib.rs

Lines changed: 65 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -41,48 +41,6 @@ pub use crate::{
4141
syntax_kind::SyntaxKind,
4242
};
4343

44-
/// Parse a prefix of the input as a given syntactic construct.
45-
///
46-
/// This is used by macro-by-example parser to implement things like `$i:item`
47-
/// and the naming of variants follows the naming of macro fragments.
48-
///
49-
/// Note that this is generally non-optional -- the result is intentionally not
50-
/// `Option<Output>`. The way MBE work, by the time we *try* to parse `$e:expr`
51-
/// we already commit to expression. In other words, this API by design can't be
52-
/// used to implement "rollback and try another alternative" logic.
53-
#[derive(Debug)]
54-
pub enum PrefixEntryPoint {
55-
Vis,
56-
Block,
57-
Stmt,
58-
Pat,
59-
Ty,
60-
Expr,
61-
Path,
62-
Item,
63-
MetaItem,
64-
}
65-
66-
impl PrefixEntryPoint {
67-
pub fn parse(&self, input: &Input) -> Output {
68-
let entry_point: fn(&'_ mut parser::Parser) = match self {
69-
PrefixEntryPoint::Vis => grammar::entry::prefix::vis,
70-
PrefixEntryPoint::Block => grammar::entry::prefix::block,
71-
PrefixEntryPoint::Stmt => grammar::entry::prefix::stmt,
72-
PrefixEntryPoint::Pat => grammar::entry::prefix::pat,
73-
PrefixEntryPoint::Ty => grammar::entry::prefix::ty,
74-
PrefixEntryPoint::Expr => grammar::entry::prefix::expr,
75-
PrefixEntryPoint::Path => grammar::entry::prefix::path,
76-
PrefixEntryPoint::Item => grammar::entry::prefix::item,
77-
PrefixEntryPoint::MetaItem => grammar::entry::prefix::meta_item,
78-
};
79-
let mut p = parser::Parser::new(input);
80-
entry_point(&mut p);
81-
let events = p.finish();
82-
event::process(events)
83-
}
84-
}
85-
8644
/// Parse the whole of the input as a given syntactic construct.
8745
///
8846
/// This covers two main use-cases:
@@ -99,9 +57,11 @@ impl PrefixEntryPoint {
9957
/// ```
10058
///
10159
/// the input to the macro will be parsed with [`PrefixEntryPoint::Item`], and
102-
/// the result will be [`TopEntryPoint::Items`].
60+
/// the result will be [`TopEntryPoint::MacroItems`].
10361
///
104-
/// This *should* (but currently doesn't) guarantee that all input is consumed.
62+
/// [`TopEntryPoint::parse`] makes a guarantee that
63+
/// * all input is consumed
64+
/// * the result is a valid tree (there's one root node)
10565
#[derive(Debug)]
10666
pub enum TopEntryPoint {
10767
SourceFile,
@@ -123,9 +83,67 @@ impl TopEntryPoint {
12383
TopEntryPoint::MacroItems => grammar::entry::top::macro_items,
12484
TopEntryPoint::Pattern => grammar::entry::top::pattern,
12585
TopEntryPoint::Type => grammar::entry::top::type_,
126-
// FIXME
127-
TopEntryPoint::Expr => grammar::entry::prefix::expr,
128-
TopEntryPoint::MetaItem => grammar::entry::prefix::meta_item,
86+
TopEntryPoint::Expr => grammar::entry::top::expr,
87+
TopEntryPoint::MetaItem => grammar::entry::top::meta_item,
88+
};
89+
let mut p = parser::Parser::new(input);
90+
entry_point(&mut p);
91+
let events = p.finish();
92+
let res = event::process(events);
93+
94+
if cfg!(debug_assertions) {
95+
let mut depth = 0;
96+
let mut first = true;
97+
for step in res.iter() {
98+
assert!(depth > 0 || first);
99+
first = false;
100+
match step {
101+
Step::Enter { .. } => depth += 1,
102+
Step::Exit => depth -= 1,
103+
Step::Token { .. } | Step::Error { .. } => (),
104+
}
105+
}
106+
assert!(!first, "no tree at all");
107+
}
108+
109+
res
110+
}
111+
}
112+
113+
/// Parse a prefix of the input as a given syntactic construct.
114+
///
115+
/// This is used by macro-by-example parser to implement things like `$i:item`
116+
/// and the naming of variants follows the naming of macro fragments.
117+
///
118+
/// Note that this is generally non-optional -- the result is intentionally not
119+
/// `Option<Output>`. The way MBE work, by the time we *try* to parse `$e:expr`
120+
/// we already commit to expression. In other words, this API by design can't be
121+
/// used to implement "rollback and try another alternative" logic.
122+
#[derive(Debug)]
123+
pub enum PrefixEntryPoint {
124+
Vis,
125+
Block,
126+
Stmt,
127+
Pat,
128+
Ty,
129+
Expr,
130+
Path,
131+
Item,
132+
MetaItem,
133+
}
134+
135+
impl PrefixEntryPoint {
136+
pub fn parse(&self, input: &Input) -> Output {
137+
let entry_point: fn(&'_ mut parser::Parser) = match self {
138+
PrefixEntryPoint::Vis => grammar::entry::prefix::vis,
139+
PrefixEntryPoint::Block => grammar::entry::prefix::block,
140+
PrefixEntryPoint::Stmt => grammar::entry::prefix::stmt,
141+
PrefixEntryPoint::Pat => grammar::entry::prefix::pat,
142+
PrefixEntryPoint::Ty => grammar::entry::prefix::ty,
143+
PrefixEntryPoint::Expr => grammar::entry::prefix::expr,
144+
PrefixEntryPoint::Path => grammar::entry::prefix::path,
145+
PrefixEntryPoint::Item => grammar::entry::prefix::item,
146+
PrefixEntryPoint::MetaItem => grammar::entry::prefix::meta_item,
129147
};
130148
let mut p = parser::Parser::new(input);
131149
entry_point(&mut p);

crates/parser/src/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
mod sourcegen_inline_tests;
2-
mod prefix_entries;
32
mod top_entries;
3+
mod prefix_entries;
44

55
use std::{
66
fmt::Write,

crates/parser/src/tests/top_entries.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ fn source_file() {
5151

5252
#[test]
5353
fn macro_stmt() {
54+
check(
55+
TopEntryPoint::MacroStmts,
56+
"",
57+
expect![[r#"
58+
MACRO_STMTS
59+
"#]],
60+
);
5461
check(
5562
TopEntryPoint::MacroStmts,
5663
"#!/usr/bin/rust",
@@ -94,6 +101,13 @@ fn macro_stmt() {
94101

95102
#[test]
96103
fn macro_items() {
104+
check(
105+
TopEntryPoint::MacroItems,
106+
"",
107+
expect![[r#"
108+
MACRO_ITEMS
109+
"#]],
110+
);
97111
check(
98112
TopEntryPoint::MacroItems,
99113
"#!/usr/bin/rust",
@@ -131,6 +145,14 @@ fn macro_items() {
131145

132146
#[test]
133147
fn macro_pattern() {
148+
check(
149+
TopEntryPoint::Pattern,
150+
"",
151+
expect![[r#"
152+
ERROR
153+
error 0: expected pattern
154+
"#]],
155+
);
134156
check(
135157
TopEntryPoint::Pattern,
136158
"Some(_)",
@@ -177,6 +199,15 @@ fn macro_pattern() {
177199

178200
#[test]
179201
fn type_() {
202+
check(
203+
TopEntryPoint::Type,
204+
"",
205+
expect![[r#"
206+
ERROR
207+
error 0: expected type
208+
"#]],
209+
);
210+
180211
check(
181212
TopEntryPoint::Type,
182213
"Option<!>",
@@ -224,6 +255,54 @@ fn type_() {
224255
);
225256
}
226257

258+
#[test]
259+
fn expr() {
260+
check(
261+
TopEntryPoint::Expr,
262+
"",
263+
expect![[r#"
264+
ERROR
265+
error 0: expected expression
266+
"#]],
267+
);
268+
check(
269+
TopEntryPoint::Expr,
270+
"2 + 2 == 5",
271+
expect![[r#"
272+
BIN_EXPR
273+
BIN_EXPR
274+
LITERAL
275+
INT_NUMBER "2"
276+
WHITESPACE " "
277+
PLUS "+"
278+
WHITESPACE " "
279+
LITERAL
280+
INT_NUMBER "2"
281+
WHITESPACE " "
282+
EQ2 "=="
283+
WHITESPACE " "
284+
LITERAL
285+
INT_NUMBER "5"
286+
"#]],
287+
);
288+
check(
289+
TopEntryPoint::Expr,
290+
"let _ = 0;",
291+
expect![[r#"
292+
ERROR
293+
LET_KW "let"
294+
WHITESPACE " "
295+
UNDERSCORE "_"
296+
WHITESPACE " "
297+
EQ "="
298+
WHITESPACE " "
299+
INT_NUMBER "0"
300+
SEMICOLON ";"
301+
error 0: expected expression
302+
"#]],
303+
);
304+
}
305+
227306
#[track_caller]
228307
fn check(entry: TopEntryPoint, input: &str, expect: expect_test::Expect) {
229308
let (parsed, _errors) = super::parse(entry, input);

0 commit comments

Comments
 (0)