diff --git a/sway-ast/src/ty/mod.rs b/sway-ast/src/ty/mod.rs index 3973a276372..7e2bbc35cab 100644 --- a/sway-ast/src/ty/mod.rs +++ b/sway-ast/src/ty/mod.rs @@ -19,7 +19,7 @@ pub enum Ty { ty: SquareBrackets>, }, Slice { - slice_token: SliceToken, + slice_token: Option, ty: SquareBrackets>, }, Ref { @@ -42,7 +42,12 @@ impl Spanned for Ty { Ty::StringArray { str_token, length } => Span::join(str_token.span(), &length.span()), Ty::Infer { underscore_token } => underscore_token.span(), Ty::Ptr { ptr_token, ty } => Span::join(ptr_token.span(), &ty.span()), - Ty::Slice { slice_token, ty } => Span::join(slice_token.span(), &ty.span()), + Ty::Slice { slice_token, ty } => { + let span = slice_token + .as_ref() + .map(|s| Span::join(s.span(), &ty.span())); + span.unwrap_or_else(|| ty.span()) + } Ty::Ref { ampersand_token, mut_token: _, diff --git a/sway-parse/src/item/item_impl.rs b/sway-parse/src/item/item_impl.rs index 3894045f264..7dc51e86fce 100644 --- a/sway-parse/src/item/item_impl.rs +++ b/sway-parse/src/item/item_impl.rs @@ -88,12 +88,28 @@ mod tests { #[test] fn parse_impl_slice() { - let item = parse::( - r#" - impl __slice[T] {} - "#, + // deprecated syntax + let item = parse::("impl __slice[T] {}"); + assert_matches!( + item.ty, + Ty::Slice { + slice_token: Some(..), + ty: _ + } ); - assert_matches!(item.ty, Ty::Slice { .. }); + + // "new" syntax + let item = parse::("impl [T] {}"); + assert_matches!( + item.ty, + Ty::Slice { + slice_token: None, + ty: _ + } + ); + + let item = parse::("impl &[T] {}"); + assert_matches!(item.ty, Ty::Ref { ty, .. } if matches!(&*ty, Ty::Slice { .. })); } #[test] diff --git a/sway-parse/src/parser.rs b/sway-parse/src/parser.rs index ad9d3e7ffc2..804ead365b3 100644 --- a/sway-parse/src/parser.rs +++ b/sway-parse/src/parser.rs @@ -207,6 +207,18 @@ impl<'a, 'e> Parser<'a, 'e> { r } + /// This method is useful if `T` does not impl `ParseToEnd` + pub fn try_parse_and_check_empty( + mut self, + append_diagnostics: bool, + ) -> ParseResult)>> { + let value = self.try_parse(append_diagnostics)?; + match self.check_empty() { + Some(consumed) => Ok(Some((value, consumed))), + None => Ok(None), + } + } + /// Parses a `T` in its canonical way. pub fn parse(&mut self) -> ParseResult { T::parse(self) @@ -223,13 +235,23 @@ impl<'a, 'e> Parser<'a, 'e> { T::parse_to_end(self) } - pub fn try_parse_to_end(mut self) -> ParseResult)>> { - let value = self.parse()?; - let consumed = match self.check_empty() { - Some(consumed) => consumed, - None => return Ok(None), + /// Do not advance the parser on failure + pub fn try_parse_to_end( + &mut self, + append_diagnostics: bool, + ) -> ParseResult<(T, ParserConsumed<'a>)> { + let handler = Handler::default(); + let fork = Parser { + token_trees: self.token_trees, + full_span: self.full_span.clone(), + handler: &handler, + check_double_underscore: self.check_double_underscore, }; - Ok(Some((value, consumed))) + let r = T::parse_to_end(fork); + if append_diagnostics { + self.handler.append(handler); + } + r } pub fn enter_delimited( diff --git a/sway-parse/src/test_utils.rs b/sway-parse/src/test_utils.rs index b0ceef2cbe4..976d2a4a958 100644 --- a/sway-parse/src/test_utils.rs +++ b/sway-parse/src/test_utils.rs @@ -1,3 +1,5 @@ +use sway_error::handler::Handler; + use crate::{priv_prelude::ParseToEnd, Parse, Parser}; use std::sync::Arc; @@ -5,11 +7,15 @@ pub fn parse(input: &str) -> T where T: Parse, { - let handler = <_>::default(); + let handler = Handler::default(); let ts = crate::token::lex(&handler, &Arc::from(input), 0, input.len(), None).unwrap(); - Parser::new(&handler, &ts) - .parse() - .unwrap_or_else(|_| panic!("Parse error: {:?}", handler.consume().0)) + let r = Parser::new(&handler, &ts).parse(); + + if handler.has_errors() || handler.has_warnings() { + panic!("{:?}", handler.consume()); + } + + r.unwrap_or_else(|_| panic!("Parse error: {:?}", handler.consume().0)) } pub fn parse_to_end(input: &str) -> T @@ -18,8 +24,11 @@ where { let handler = <_>::default(); let ts = crate::token::lex(&handler, &Arc::from(input), 0, input.len(), None).unwrap(); - Parser::new(&handler, &ts) - .parse_to_end() - .map(|(m, _)| m) - .unwrap_or_else(|_| panic!("Parse error: {:?}", handler.consume().0)) + let r = Parser::new(&handler, &ts).parse_to_end().map(|(m, _)| m); + + if handler.has_errors() || handler.has_warnings() { + panic!("{:?}", handler.consume()); + } + + r.unwrap_or_else(|_| panic!("Parse error: {:?}", handler.consume().0)) } diff --git a/sway-parse/src/ty/mod.rs b/sway-parse/src/ty/mod.rs index dcba9b74ef1..b01153da595 100644 --- a/sway-parse/src/ty/mod.rs +++ b/sway-parse/src/ty/mod.rs @@ -1,7 +1,6 @@ use crate::{Parse, ParseBracket, ParseResult, ParseToEnd, Parser, ParserConsumed}; - use sway_ast::brackets::{Parens, SquareBrackets}; -use sway_ast::keywords::{DoubleColonToken, OpenAngleBracketToken}; +use sway_ast::keywords::{DoubleColonToken, OpenAngleBracketToken, PtrToken, SliceToken}; use sway_ast::ty::{Ty, TyArrayDescriptor, TyTupleDescriptor}; use sway_error::parser_error::ParseErrorKind; use sway_types::{ast::Delimiter, Ident}; @@ -30,9 +29,24 @@ impl Parse for Ty { return Err(parser .emit_error(ParseErrorKind::ExpectedCommaOrCloseParenInTupleOrParenExpression)); } - if let Some(descriptor) = SquareBrackets::try_parse(parser)? { - return Ok(Ty::Array(descriptor)); - }; + + if let Some((mut inner_parser, span)) = parser.enter_delimited(Delimiter::Bracket) { + // array like [type; len] + if let Ok((array, _)) = inner_parser.try_parse_to_end::(false) { + return Ok(Ty::Array(SquareBrackets { inner: array, span })); + } + + // slice like [type] + if let Ok(Some((ty, _))) = inner_parser.try_parse_and_check_empty::(false) { + return Ok(Ty::Slice { + slice_token: None, + ty: SquareBrackets { + inner: Box::new(ty), + span, + }, + }); + } + } if let Some(str_token) = parser.take() { let length = SquareBrackets::try_parse_all_inner(parser, |mut parser| { @@ -48,18 +62,26 @@ impl Parse for Ty { if let Some(underscore_token) = parser.take() { return Ok(Ty::Infer { underscore_token }); } - if let Some(ptr_token) = parser.take() { + + if let Some(ptr_token) = parser.take::() { let ty = SquareBrackets::parse_all_inner(parser, |mut parser| { parser.emit_error(ParseErrorKind::UnexpectedTokenAfterPtrType) })?; return Ok(Ty::Ptr { ptr_token, ty }); } - if let Some(slice_token) = parser.take() { - let ty = SquareBrackets::parse_all_inner(parser, |mut parser| { + + // slice like __slice[type] + // TODO: deprecate this syntax (see https://github.com/FuelLabs/sway/issues/5110) + if let Some(slice_token) = parser.take::() { + let ty = SquareBrackets::>::parse_all_inner(parser, |mut parser| { parser.emit_error(ParseErrorKind::UnexpectedTokenAfterSliceType) })?; - return Ok(Ty::Slice { slice_token, ty }); + return Ok(Ty::Slice { + slice_token: Some(slice_token), + ty, + }); } + if let Some(ampersand_token) = parser.take() { let mut_token = parser.take(); let ty = Box::new(parser.parse()?); @@ -69,9 +91,11 @@ impl Parse for Ty { ty, }); } + if let Some(bang_token) = parser.take() { return Ok(Ty::Never { bang_token }); } + if parser.peek::().is_some() || parser.peek::().is_some() || parser.peek::().is_some() @@ -79,6 +103,7 @@ impl Parse for Ty { let path_type = parser.parse()?; return Ok(Ty::Path(path_type)); } + Err(parser.emit_error(ParseErrorKind::ExpectedType)) } } @@ -121,14 +146,24 @@ mod tests { assert_matches!(item, Ty::Ptr { .. }); } + #[test] + fn parse_array() { + let item = parse::("[T; 1]"); + assert_matches!(item, Ty::Array { .. }); + } + #[test] fn parse_slice() { - let item = parse::( - r#" - __slice[T] - "#, - ); + // deprecated syntax + let item = parse::("__slice[T]"); assert_matches!(item, Ty::Slice { .. }); + + // " new" syntax + let item = parse::("[T]"); + assert_matches!(item, Ty::Slice { .. }); + + let item = parse::("&[T]"); + assert_matches!(item, Ty::Ref { ty, .. } if matches!(&*ty, Ty::Slice { .. })); } #[test] diff --git a/swayfmt/src/utils/language/ty.rs b/swayfmt/src/utils/language/ty.rs index 1d764e83bd7..a509cca1567 100644 --- a/swayfmt/src/utils/language/ty.rs +++ b/swayfmt/src/utils/language/ty.rs @@ -117,15 +117,20 @@ fn format_ptr( fn format_slice( formatted_code: &mut FormattedCode, - slice_token: SliceToken, + slice_token: Option, ty: SquareBrackets>, ) -> Result<(), FormatterError> { - write!( - formatted_code, - "{}[{}]", - slice_token.span().as_str(), - ty.into_inner().span().as_str() - )?; + if let Some(slice_token) = slice_token { + write!( + formatted_code, + "{}[{}]", + slice_token.span().as_str(), + ty.into_inner().span().as_str() + )?; + } else { + write!(formatted_code, "[{}]", ty.into_inner().span().as_str())?; + } + Ok(()) } @@ -202,7 +207,11 @@ impl LeafSpans for Ty { collected_spans } Ty::Slice { slice_token, ty } => { - let mut collected_spans = vec![ByteSpan::from(slice_token.span())]; + let mut collected_spans = if let Some(slice_token) = slice_token { + vec![ByteSpan::from(slice_token.span())] + } else { + vec![] + }; collected_spans.append(&mut ty.leaf_spans()); collected_spans } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/reexport/aliases/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/reexport/aliases/json_abi_oracle_new_encoding.json old mode 100755 new mode 100644 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/reexport/multiple_imports_of_same_reexport/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/reexport/multiple_imports_of_same_reexport/json_abi_oracle_new_encoding.json old mode 100755 new mode 100644 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/reexport/reexport_paths/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/reexport/reexport_paths/json_abi_oracle_new_encoding.json old mode 100755 new mode 100644 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/reexport/shadowing_in_reexporting_module/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/reexport/shadowing_in_reexporting_module/json_abi_oracle_new_encoding.json old mode 100755 new mode 100644 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/reexport/simple_glob_import/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/reexport/simple_glob_import/json_abi_oracle_new_encoding.json old mode 100755 new mode 100644 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/reexport/simple_item_import/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/reexport/simple_item_import/json_abi_oracle_new_encoding.json old mode 100755 new mode 100644 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/reexport/visibility/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/reexport/visibility/json_abi_oracle_new_encoding.json old mode 100755 new mode 100644