Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parser recovery inside trait/abi #4979

Merged
merged 13 commits into from
Aug 23, 2023
2 changes: 1 addition & 1 deletion sway-ast/src/item/item_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub struct ItemAbi {
pub abi_token: AbiToken,
pub name: Ident,
pub super_traits: Option<(ColonToken, Traits)>,
pub abi_items: Braces<Vec<(Annotated<ItemTraitItem>, SemicolonToken)>>,
pub abi_items: Braces<Vec<Annotated<ItemTraitItem>>>,
pub abi_defs_opt: Option<Braces<Vec<Annotated<ItemFn>>>>,
}

Expand Down
23 changes: 18 additions & 5 deletions sway-ast/src/item/item_trait.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use sway_error::handler::ErrorEmitted;

use crate::priv_prelude::*;

#[derive(Clone, Debug, Serialize)]
pub enum ItemTraitItem {
Fn(FnSignature),
Const(ItemConst),
Fn(FnSignature, Option<SemicolonToken>),
Const(ItemConst, Option<SemicolonToken>),
// to handle parser recovery: Error represents an incomplete trait item
Error(Box<[Span]>, #[serde(skip_serializing)] ErrorEmitted),
}

#[derive(Clone, Debug, Serialize)]
Expand All @@ -14,7 +18,7 @@ pub struct ItemTrait {
pub generics: Option<GenericParams>,
pub where_clause_opt: Option<WhereClause>,
pub super_traits: Option<(ColonToken, Traits)>,
pub trait_items: Braces<Vec<(Annotated<ItemTraitItem>, SemicolonToken)>>,
pub trait_items: Braces<Vec<Annotated<ItemTraitItem>>>,
pub trait_defs_opt: Option<Braces<Vec<Annotated<ItemFn>>>>,
}

Expand All @@ -35,8 +39,17 @@ impl Spanned for ItemTrait {
impl Spanned for ItemTraitItem {
fn span(&self) -> Span {
match self {
ItemTraitItem::Fn(fn_decl) => fn_decl.span(),
ItemTraitItem::Const(const_decl) => const_decl.span(),
ItemTraitItem::Fn(fn_decl, semicolon) => match semicolon.as_ref().map(|x| x.span()) {
Some(semicolon) => Span::join(fn_decl.span(), semicolon),
None => fn_decl.span(),
},
ItemTraitItem::Const(const_decl, semicolon) => {
match semicolon.as_ref().map(|x| x.span()) {
Some(semicolon) => Span::join(const_decl.span(), semicolon),
None => const_decl.span(),
}
}
ItemTraitItem::Error(spans, _) => Span::join_all(spans.iter().cloned()),
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions sway-core/src/language/parsed/declaration/trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ use super::{ConstantDeclaration, FunctionDeclaration, FunctionParameter};
use crate::{
decl_engine::DeclRefTrait, engine_threading::*, language::*, transform, type_system::*,
};
use sway_error::handler::ErrorEmitted;
use sway_types::{ident::Ident, span::Span, Spanned};

#[derive(Debug, Clone)]
pub enum TraitItem {
TraitFn(TraitFn),
Constant(ConstantDeclaration),
// to handle parser recovery: Error represents an incomplete trait item
Error(Box<[Span]>, ErrorEmitted),
}

#[derive(Debug, Clone)]
Expand Down
3 changes: 3 additions & 0 deletions sway-core/src/semantic_analysis/ast_node/declaration/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ impl ty::TyAbiDecl {

const_name
}
TraitItem::Error(_, _) => {
continue;
}
};

if !ids.insert(decl_name.clone()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ impl ty::TyTraitDecl {

const_name
}
TraitItem::Error(_, _) => {
continue;
}
};

if !ids.insert(decl_name.clone()) {
Expand Down
2 changes: 2 additions & 0 deletions sway-core/src/semantic_analysis/node_dependencies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ impl Dependencies {
TraitItem::Constant(const_decl) => {
deps.gather_from_constant_decl(engines, const_decl)
}
TraitItem::Error(_, _) => deps,
})
.gather_from_iter(methods.iter(), |deps, fn_decl| {
deps.gather_from_fn_decl(engines, fn_decl)
Expand Down Expand Up @@ -395,6 +396,7 @@ impl Dependencies {
TraitItem::Constant(const_decl) => {
deps.gather_from_constant_decl(engines, const_decl)
}
TraitItem::Error(_, _) => deps,
})
.gather_from_iter(methods.iter(), |deps, fn_decl| {
deps.gather_from_fn_decl(engines, fn_decl)
Expand Down
14 changes: 8 additions & 6 deletions sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -586,20 +586,21 @@ fn item_trait_to_trait_declaration(
.trait_items
.into_inner()
.into_iter()
.map(|(annotated, _)| {
.map(|annotated| {
let attributes = item_attrs_to_map(context, handler, &annotated.attribute_list)?;
if !cfg_eval(context, handler, &attributes)? {
return Ok(None);
}
Ok(Some(match annotated.value {
ItemTraitItem::Fn(fn_sig) => {
ItemTraitItem::Fn(fn_sig, _) => {
fn_signature_to_trait_fn(context, handler, engines, fn_sig, attributes)
.map(TraitItem::TraitFn)
}
ItemTraitItem::Const(const_decl) => item_const_to_constant_declaration(
ItemTraitItem::Const(const_decl, _) => item_const_to_constant_declaration(
context, handler, engines, const_decl, attributes, false,
)
.map(TraitItem::Constant),
ItemTraitItem::Error(spans, error) => Ok(TraitItem::Error(spans, error)),
}?))
})
.filter_map_ok(|item| item)
Expand Down Expand Up @@ -759,14 +760,14 @@ fn item_abi_to_abi_declaration(
.abi_items
.into_inner()
.into_iter()
.map(|(annotated, _)| {
.map(|annotated| {
let attributes =
item_attrs_to_map(context, handler, &annotated.attribute_list)?;
if !cfg_eval(context, handler, &attributes)? {
return Ok(None);
}
Ok(Some(match annotated.value {
ItemTraitItem::Fn(fn_signature) => {
ItemTraitItem::Fn(fn_signature, _) => {
let trait_fn = fn_signature_to_trait_fn(
context,
handler,
Expand All @@ -783,10 +784,11 @@ fn item_abi_to_abi_declaration(
)?;
Ok(TraitItem::TraitFn(trait_fn))
}
ItemTraitItem::Const(const_decl) => item_const_to_constant_declaration(
ItemTraitItem::Const(const_decl, _) => item_const_to_constant_declaration(
context, handler, engines, const_decl, attributes, false,
)
.map(TraitItem::Constant),
ItemTraitItem::Error(spans, error) => Ok(TraitItem::Error(spans, error)),
}?))
})
.filter_map_ok(|item| item)
Expand Down
1 change: 0 additions & 1 deletion sway-error/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,6 @@ pub enum CompileError {
},
#[error("This opcode takes an immediate value but none was provided.")]
MissingImmediate { span: Span },

#[error("This immediate value is invalid.")]
InvalidImmediateValue { span: Span },
#[error("Variant \"{variant_name}\" does not exist on enum \"{enum_name}\"")]
Expand Down
2 changes: 1 addition & 1 deletion sway-error/src/parser_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub enum ParseErrorKind {
ExpectedImportNameGroupOrGlob,
#[error("Expected an item.")]
ExpectedAnItem,
#[error("Expected item after doc comment.")]
#[error("Expected an item after doc comment.")]
ExpectedAnItemAfterDocComment,
#[error("Expected a comma or closing parenthesis in function arguments.")]
ExpectedCommaOrCloseParenInFnArgs,
Expand Down
14 changes: 8 additions & 6 deletions sway-lsp/src/traverse/lexed_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,10 @@ impl Parse for ItemTrait {
self.trait_items
.get()
.iter()
.for_each(|(annotated, _)| match &annotated.value {
sway_ast::ItemTraitItem::Fn(fn_sig) => fn_sig.parse(ctx),
sway_ast::ItemTraitItem::Const(item_const) => item_const.parse(ctx),
.for_each(|annotated| match &annotated.value {
sway_ast::ItemTraitItem::Fn(fn_sig, _) => fn_sig.parse(ctx),
sway_ast::ItemTraitItem::Const(item_const, _) => item_const.parse(ctx),
sway_ast::ItemTraitItem::Error(_, _) => {}
});

if let Some(trait_defs_opt) = &self.trait_defs_opt {
Expand Down Expand Up @@ -345,9 +346,10 @@ impl Parse for ItemAbi {
self.abi_items
.get()
.iter()
.for_each(|(annotated, _)| match &annotated.value {
sway_ast::ItemTraitItem::Fn(fn_sig) => fn_sig.parse(ctx),
sway_ast::ItemTraitItem::Const(item_const) => item_const.parse(ctx),
.for_each(|annotated| match &annotated.value {
sway_ast::ItemTraitItem::Fn(fn_sig, _) => fn_sig.parse(ctx),
sway_ast::ItemTraitItem::Const(item_const, _) => item_const.parse(ctx),
sway_ast::ItemTraitItem::Error(_, _) => {}
});

if let Some(abi_defs_opt) = self.abi_defs_opt.as_ref() {
Expand Down
2 changes: 2 additions & 0 deletions sway-lsp/src/traverse/parsed_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,7 @@ impl Parse for TraitDeclaration {
self.interface_surface.iter().for_each(|item| match item {
TraitItem::TraitFn(trait_fn) => trait_fn.parse(ctx),
TraitItem::Constant(const_decl) => const_decl.parse(ctx),
TraitItem::Error(_, _) => {}
});
self.methods.iter().for_each(|func_dec| {
func_dec.parse(ctx);
Expand Down Expand Up @@ -848,6 +849,7 @@ impl Parse for AbiDeclaration {
self.interface_surface.iter().for_each(|item| match item {
TraitItem::TraitFn(trait_fn) => trait_fn.parse(ctx),
TraitItem::Constant(const_decl) => const_decl.parse(ctx),
TraitItem::Error(_, _) => {}
});
self.supertraits.iter().for_each(|supertrait| {
supertrait.parse(ctx);
Expand Down
2 changes: 1 addition & 1 deletion sway-parse/src/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ impl ParseToEnd for CodeBlockContents {
break (None, consumed);
}

match parser.parse_fn_with_recovery(parse_stmt) {
match parser.call_parsing_function_with_recovery(parse_stmt) {
Ok(StmtOrTail::Stmt(s)) => statements.push(s),
Ok(StmtOrTail::Tail(e, c)) => break (Some(e), c),
Err(r) => {
Expand Down
8 changes: 5 additions & 3 deletions sway-parse/src/item/item_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ impl Parse for ItemAbi {
}
None => None,
};
let abi_items: Braces<Vec<(Annotated<ItemTraitItem>, _)>> = parser.parse()?;
for (annotated, _) in abi_items.get().iter() {

let abi_items: Braces<Vec<Annotated<ItemTraitItem>>> = parser.parse()?;
for annotated in abi_items.get().iter() {
#[allow(irrefutable_let_patterns)]
if let ItemTraitItem::Fn(fn_signature) = &annotated.value {
if let ItemTraitItem::Fn(fn_signature, _) = &annotated.value {
parser.ban_visibility_qualifier(&fn_signature.visibility)?;
}
}

let abi_defs_opt: Option<Braces<Vec<Annotated<ItemFn>>>> = Braces::try_parse(parser)?;
if let Some(abi_defs) = &abi_defs_opt {
for item_fn in abi_defs.get().iter() {
Expand Down
22 changes: 17 additions & 5 deletions sway-parse/src/item/item_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,26 @@ impl Parse for ItemTraitItem {
fn parse(parser: &mut Parser) -> ParseResult<ItemTraitItem> {
if parser.peek::<PubToken>().is_some() || parser.peek::<FnToken>().is_some() {
let fn_decl = parser.parse()?;
Ok(ItemTraitItem::Fn(fn_decl))
let semicolon = parser.parse().ok();
Ok(ItemTraitItem::Fn(fn_decl, semicolon))
} else if let Some(_const_keyword) = parser.peek::<ConstToken>() {
let const_decl = parser.parse()?;
Ok(ItemTraitItem::Const(const_decl))
let semicolon = parser.parse().ok();
Ok(ItemTraitItem::Const(const_decl, semicolon))
} else {
Err(parser.emit_error(ParseErrorKind::ExpectedAnItem))
}
}

fn error(
spans: Box<[sway_types::Span]>,
error: sway_error::handler::ErrorEmitted,
) -> Option<Self>
where
Self: Sized,
{
Some(ItemTraitItem::Error(spans, error))
}
}

impl Parse for ItemTrait {
Expand All @@ -34,9 +46,9 @@ impl Parse for ItemTrait {
};
let where_clause_opt = parser.guarded_parse::<WhereToken, _>()?;

let trait_items: Braces<Vec<(Annotated<ItemTraitItem>, _)>> = parser.parse()?;
for (annotated, _) in trait_items.get().iter() {
if let ItemTraitItem::Fn(fn_sig) = &annotated.value {
let trait_items: Braces<Vec<Annotated<ItemTraitItem>>> = parser.parse()?;
for item in trait_items.get().iter() {
if let ItemTraitItem::Fn(fn_sig, _) = &item.value {
parser.ban_visibility_qualifier(&fn_sig.visibility)?;
}
}
Expand Down
8 changes: 4 additions & 4 deletions sway-parse/src/item/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,8 +518,8 @@ mod tests {

let trait_item = decls.next();
assert!(trait_item.is_some());
let (annotated, _) = trait_item.unwrap();
if let ItemTraitItem::Fn(_fn_sig) = &annotated.value {
let annotated = trait_item.unwrap();
if let ItemTraitItem::Fn(_fn_sig, _) = &annotated.value {
assert_eq!(
attributes(&annotated.attribute_list),
vec![[("foo", Some(vec!["one"]))], [("bar", None)]]
Expand Down Expand Up @@ -575,15 +575,15 @@ mod tests {
assert!(f_sig.is_some());

assert_eq!(
attributes(&f_sig.unwrap().0.attribute_list),
attributes(&f_sig.unwrap().attribute_list),
vec![[("bar", Some(vec!["one", "two", "three"]))],]
);

let g_sig = decls.next();
assert!(g_sig.is_some());

assert_eq!(
attributes(&g_sig.unwrap().0.attribute_list),
attributes(&g_sig.unwrap().attribute_list),
vec![[("foo", None)],]
);
assert!(decls.next().is_none());
Expand Down
4 changes: 3 additions & 1 deletion sway-parse/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use sway_error::parser_error::ParseErrorKind;
use sway_types::{ast::Delimiter, Ident, Spanned};

pub trait Parse {
const FALLBACK_ERROR: ParseErrorKind = ParseErrorKind::InvalidItem;

fn parse(parser: &mut Parser) -> ParseResult<Self>
where
Self: Sized;
Expand Down Expand Up @@ -110,7 +112,7 @@ where
Ok(value) => ret.push(value),
Err(r) => {
let (spans, error) =
r.recover_at_next_line_with_fallback_error(ParseErrorKind::InvalidItem);
r.recover_at_next_line_with_fallback_error(T::FALLBACK_ERROR);
if let Some(error) = T::error(spans, error) {
ret.push(error);
} else {
Expand Down
Loading