mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-26 20:09:19 +00:00
check top level entry point invariants
This commit is contained in:
parent
fa049d94d1
commit
d846afdeef
5 changed files with 42 additions and 12 deletions
|
@ -105,21 +105,21 @@ macro_rules! m2 { ($x:ident) => {} }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expansion_does_not_parse_as_expression() {
|
fn expansion_does_not_parse_as_expression() {
|
||||||
cov_mark::check!(expansion_does_not_parse_as_expression);
|
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
macro_rules! stmts {
|
macro_rules! stmts {
|
||||||
() => { let _ = 0; }
|
() => { let _ = 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn f() { let _ = stmts!(); }
|
fn f() { let _ = stmts!/*+errors*/(); }
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
macro_rules! stmts {
|
macro_rules! stmts {
|
||||||
() => { let _ = 0; }
|
() => { let _ = 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn f() { let _ = /* error: could not convert tokens */; }
|
fn f() { let _ = /* parse error: expected expression */
|
||||||
|
let _ = 0;; }
|
||||||
"#]],
|
"#]],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1148,7 +1148,7 @@ fn foo() { let a = id!([0u32, bar($0)] ); }
|
||||||
fn test_hover_through_literal_string_in_macro() {
|
fn test_hover_through_literal_string_in_macro() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
macro_rules! arr { ($($tt:tt)*) => { [$($tt)*)] } }
|
macro_rules! arr { ($($tt:tt)*) => { [$($tt)*] } }
|
||||||
fn foo() {
|
fn foo() {
|
||||||
let mastered_for_itunes = "";
|
let mastered_for_itunes = "";
|
||||||
let _ = arr!("Tr$0acks", &mastered_for_itunes);
|
let _ = arr!("Tr$0acks", &mastered_for_itunes);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Conversions between [`SyntaxNode`] and [`tt::TokenTree`].
|
//! Conversions between [`SyntaxNode`] and [`tt::TokenTree`].
|
||||||
|
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use stdx::non_empty_vec::NonEmptyVec;
|
use stdx::{never, non_empty_vec::NonEmptyVec};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, make::tokens::doc_comment},
|
ast::{self, make::tokens::doc_comment},
|
||||||
AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement, SyntaxKind,
|
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()),
|
parser::Step::Error { msg } => tree_sink.error(msg.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if tree_sink.roots.len() != 1 {
|
if never!(tree_sink.roots.len() != 1) {
|
||||||
cov_mark::hit!(expansion_does_not_parse_as_expression);
|
|
||||||
return Err(ExpandError::ConversionError);
|
return Err(ExpandError::ConversionError);
|
||||||
}
|
}
|
||||||
//FIXME: would be cool to report errors
|
//FIXME: would be cool to report errors
|
||||||
|
|
|
@ -148,6 +148,19 @@ pub(crate) mod entry {
|
||||||
}
|
}
|
||||||
m.complete(p, ERROR);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,9 +99,11 @@ impl PrefixEntryPoint {
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// the input to the macro will be parsed with [`PrefixEntryPoint::Item`], and
|
/// 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)]
|
#[derive(Debug)]
|
||||||
pub enum TopEntryPoint {
|
pub enum TopEntryPoint {
|
||||||
SourceFile,
|
SourceFile,
|
||||||
|
@ -124,13 +126,29 @@ impl TopEntryPoint {
|
||||||
TopEntryPoint::Pattern => grammar::entry::top::pattern,
|
TopEntryPoint::Pattern => grammar::entry::top::pattern,
|
||||||
TopEntryPoint::Type => grammar::entry::top::type_,
|
TopEntryPoint::Type => grammar::entry::top::type_,
|
||||||
TopEntryPoint::Expr => grammar::entry::top::expr,
|
TopEntryPoint::Expr => grammar::entry::top::expr,
|
||||||
// FIXME
|
TopEntryPoint::MetaItem => grammar::entry::top::meta_item,
|
||||||
TopEntryPoint::MetaItem => grammar::entry::prefix::meta_item,
|
|
||||||
};
|
};
|
||||||
let mut p = parser::Parser::new(input);
|
let mut p = parser::Parser::new(input);
|
||||||
entry_point(&mut p);
|
entry_point(&mut p);
|
||||||
let events = p.finish();
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue