Skip to content

Commit fd9359b

Browse files
committed
PoC: Initial pass of recovery from no matches
This relies on using almost entirely skipped parse rules to stop at terminators such ';' in the contract members list. In the future, we'd need to expand the recovery boundary token set and handle that appropriately ourselves.
1 parent 5e0415e commit fd9359b

75 files changed

Lines changed: 1386 additions & 613 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/codegen/parser/runtime/src/kinds.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ pub enum TokenKind {
1919
XXX,
2020
}
2121

22+
impl TokenKind {
23+
pub fn is_whitespace(&self) -> bool {
24+
unreachable!("Expanded by the template")
25+
}
26+
}
27+
2228
#[derive(
2329
Debug,
2430
Eq,

crates/codegen/parser/runtime/src/support/choice_helper.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,24 @@ impl ChoiceHelper {
5454
running.expected_tokens.extend(next.expected_tokens.clone());
5555
false
5656
}
57+
// TODO: Add a comment
58+
(ParserResult::NoMatch(_), ParserResult::SkippedUntil(next)) => {
59+
// HACK: Make this more elegant - either not require checking for trivia here
60+
// or make it also work in the presence of comments
61+
// NOTE: We currently overeagerly try to recover from NoMatch, leading to absurd recoveries
62+
// such as parsing "using A for B;" as (almost entirely skipped) pragma directive, only because
63+
// it ends with and matches ";".
64+
// Once we have a better way to limit the recovery boundary to reasonable set of tokens
65+
// such as ';' for contract members, we should move this check to `recovery.rs`
66+
// to not rely on wholly skipped rules to serve as our recovery boundary.
67+
let only_trivia = next
68+
.nodes
69+
.descendents()
70+
.filter_map(cst::Node::as_token)
71+
.all(|token| token.kind.is_whitespace());
72+
73+
!only_trivia && next.skipped.len() > 0
74+
}
5775
// Otherwise, we have some match and we ignore a missing next one.
5876
(ParserResult::IncompleteMatch(..), ParserResult::NoMatch(..))
5977
| (ParserResult::SkippedUntil(..), ParserResult::NoMatch(..))

crates/codegen/parser/runtime/src/support/recovery.rs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,18 @@ impl ParserResult {
4747
token
4848
};
4949

50-
let (mut nodes, mut expected_tokens, is_incomplete) = match self {
51-
ParserResult::IncompleteMatch(result) => (result.nodes, result.expected_tokens, true),
50+
enum MatchKind {
51+
Match,
52+
Incomplete,
53+
No,
54+
}
55+
let (mut nodes, mut expected_tokens, match_kind) = match self {
56+
ParserResult::NoMatch(result) => (vec![], result.expected_tokens, MatchKind::No),
57+
ParserResult::IncompleteMatch(result) => {
58+
(result.nodes, result.expected_tokens, MatchKind::Incomplete)
59+
}
5260
ParserResult::Match(result) if peek_token_after_trivia() != Some(expected) => {
53-
(result.nodes, result.expected_tokens, false)
61+
(result.nodes, result.expected_tokens, MatchKind::Match)
5462
}
5563
// No need to recover, so just return as-is.
5664
_ => return self,
@@ -69,15 +77,24 @@ impl ParserResult {
6977
if local_delims.is_empty()
7078
&& (token == expected || input.closing_delimiters().contains(&token)) =>
7179
{
80+
let skipped_range = start..save;
81+
82+
// If there's no match and we only skipped through trivia, we didn't actually
83+
// recover, so return as-is to not attempt to recover.
84+
if skipped_range.is_empty() && matches!(match_kind, MatchKind::No) {
85+
input.set_position(before_recovery);
86+
return ParserResult::no_match(expected_tokens);
87+
}
88+
7289
nodes.extend(leading_trivia);
73-
if !is_incomplete {
90+
if matches!(match_kind, MatchKind::Match) {
91+
} else {
7492
expected_tokens.push(expected);
7593
}
7694

7795
// Don't consume the delimiter; parent will consume it
7896
input.set_position(save);
7997

80-
let skipped_range = start..save;
8198
input.emit(ParseError {
8299
text_range: skipped_range.clone(),
83100
tokens_that_would_have_allowed_more_progress: expected_tokens.clone(),
@@ -106,10 +123,12 @@ impl ParserResult {
106123
None => {
107124
input.set_position(before_recovery);
108125

109-
if is_incomplete {
110-
return ParserResult::incomplete_match(nodes, expected_tokens);
111-
} else {
112-
return ParserResult::r#match(nodes, expected_tokens);
126+
match match_kind {
127+
MatchKind::Match => return ParserResult::r#match(nodes, expected_tokens),
128+
MatchKind::Incomplete => {
129+
return ParserResult::incomplete_match(nodes, expected_tokens)
130+
}
131+
MatchKind::No => return ParserResult::no_match(expected_tokens),
113132
}
114133
}
115134
}

crates/codegen/parser/runtime/src/templates/kinds.rs.jinja2

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ pub enum TokenKind {
7373
{%- endfor -%}
7474
}
7575

76+
impl TokenKind {
77+
pub fn is_whitespace(&self) -> bool {
78+
matches!(self, TokenKind::Whitespace | TokenKind::EndOfLine)
79+
}
80+
}
81+
7682
#[derive(strum_macros::FromRepr)]
7783
/// The lexical context of the scanner.
7884
#[cfg_attr(feature = "slang_napi_interfaces", /* derives `Clone` and `Copy` */ napi(string_enum, namespace = "language"))]

crates/solidity/outputs/cargo/crate/src/generated/kinds.rs

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/solidity/outputs/cargo/crate/src/generated/support/choice_helper.rs

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/solidity/outputs/cargo/crate/src/generated/support/recovery.rs

Lines changed: 28 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/solidity/outputs/npm/crate/src/generated/kinds.rs

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/solidity/outputs/npm/crate/src/generated/support/choice_helper.rs

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/solidity/outputs/npm/crate/src/generated/support/recovery.rs

Lines changed: 28 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)