Skip to content
Merged
23 changes: 23 additions & 0 deletions sway-error/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2724,6 +2724,29 @@ impl ToDiagnostic for CompileError {
},
Parse { error } => {
match &error.kind {
ParseErrorKind::MissingColonInEnumTypeField { variant_name, tuple_contents } => Diagnostic {
reason: Some(Reason::new(code(1), "Enum variant declaration is not valid".to_string())),
issue: Issue::error(
source_engine,
error.span.clone(),
format!("`{}` is not a valid enum variant declaration.", error.span.as_str()),
),
hints: vec![
if let Some(tuple_contents) = tuple_contents {
Hint::help(
source_engine,
error.span.clone(),
format!("Did you mean `{}: ({})`?", variant_name, tuple_contents.as_str())
)
} else {
Hint::none()
}
],
help: vec![
"In Sway, enum variants are in the form `Variant: ()`, `Variant: <type>`, or `Variant: (<type1>, ..., <typeN>)`.".to_string(),
"E.g., `Foo: (), `Bar: u64`, or `Bar: (bool, u32)`.".to_string(),
],
},
ParseErrorKind::UnassignableExpression { erroneous_expression_kind, erroneous_expression_span } => Diagnostic {
reason: Some(Reason::new(code(1), "Expression cannot be assigned to".to_string())),
// A bit of a special handling for parentheses, because they are the only
Expand Down
5 changes: 4 additions & 1 deletion sway-error/src/parser_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@ pub enum ParseErrorKind {
#[error("Expected a path type.")]
ExpectedPathType,
#[error("Expected ':'. Enum variants must be in the form `Variant: ()`, `Variant: <type>`, or `Variant: (<type1>, ..., <typeN>)`. E.g., `Foo: (), or `Bar: (bool, u32)`.")]
MissingColonInEnumTypeField,
MissingColonInEnumTypeField {
variant_name: Ident,
tuple_contents: Option<Span>,
},
#[error("Expected storage key of type U256.")]
ExpectedStorageKeyU256,
}
Expand Down
73 changes: 63 additions & 10 deletions sway-parse/src/item/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use sway_ast::{
ItemTypeAlias, ItemUse, Submodule, TraitType, TypeField,
};
use sway_error::parser_error::ParseErrorKind;
use sway_types::ast::Delimiter;
use sway_types::{Ident, Span, Spanned};

mod item_abi;
mod item_configurable;
Expand Down Expand Up @@ -95,16 +97,67 @@ impl Parse for ItemKind {
impl Parse for TypeField {
fn parse(parser: &mut Parser) -> ParseResult<TypeField> {
let visibility = parser.take();
Ok(TypeField {
visibility,
name: parser.parse()?,
colon_token: if parser.peek::<ColonToken>().is_some() {
parser.parse()
} else {
Err(parser.emit_error(ParseErrorKind::MissingColonInEnumTypeField))
}?,
ty: parser.parse()?,
})
let name: Ident = parser.parse()?;
let name_span = name.span();

// Check for the common, valid case `Variant: Type` first.
if parser.peek::<ColonToken>().is_some() {
let colon_token = parser.parse()?;
let ty = parser.parse()?;
return Ok(TypeField {
visibility,
name,
colon_token,
ty,
});
}

// --- Colon was not found after name, proceed with error handling ---

// Case 1: Check for invalid struct-like variant `Variant { ... }`
if let Some((_inner_parser, brace_span)) = parser.enter_delimited(Delimiter::Brace) {
let error_span = Span::join(name_span, &brace_span);
return Err(parser.emit_error_with_span(
ParseErrorKind::MissingColonInEnumTypeField {
variant_name: name,
tuple_contents: None, // Not a tuple issue
},
error_span,
));
}

// Case 2: Check for invalid tuple-like variant `Variant ( ... )` (missing colon)
if let Some((inner_parser, paren_span)) = parser.enter_delimited(Delimiter::Parenthesis) {
let tuple_contents_span = inner_parser.full_span().clone();
let error_span = Span::join(name_span.clone(), &paren_span);
return Err(parser.emit_error_with_span(
ParseErrorKind::MissingColonInEnumTypeField {
variant_name: name,
tuple_contents: Some(tuple_contents_span),
},
error_span,
));
}

// Case 3: Check for unit-like variant `Variant,` or `Variant` (at end)
if parser.is_empty() || parser.peek::<sway_ast::CommaToken>().is_some() {
return Err(parser.emit_error_with_span(
ParseErrorKind::MissingColonInEnumTypeField {
variant_name: name,
tuple_contents: Some(Span::dummy()), // Indicate unit-like
},
name_span,
));
}

// Case 4: Something else follows where a colon was expected
Err(parser.emit_error_with_span(
ParseErrorKind::MissingColonInEnumTypeField {
variant_name: name,
tuple_contents: None,
},
name_span,
))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ enum Enum2 {
G(u32, u32), // Also illegal, but shadowed by previous error
}

enum Enum3 {
F(bool, u32, str[4]), // Illegal
G(u32, u32), // Also illegal, but shadowed by previous error
}

enum Enum4 {
A { x: i32, y: i32 }, // Illegal
Another(u64, bool), // Also illegal, but shadowed by previous error
}

fn main() {
}

Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
category = "fail"

# check: $()error
# check: enum_rust_like/src/main.sw:7:5
# check: enum_rust_like/src/main.sw:7:3
# check: $()Ok, // Illegal
# nextln: $()Expected ':'. Enum variants must be in the form `Variant: ()`, `Variant: <type>`, or `Variant: (<type1>, ..., <typeN>)`. E.g., `Foo: (), or `Bar: (bool, u32)`.
# nextln: $()`Ok` is not a valid enum variant declaration.
# nextln: $()Did you mean `Ok: ()`?
# check: $()In Sway, enum variants are in the form `Variant: ()`, `Variant: <type>`, or `Variant: (<type1>, ..., <typeN>)`.
# nextln: $()E.g., `Foo: (), `Bar: u64`, or `Bar: (bool, u32)`.

# check: $()error
# check: enum_rust_like/src/main.sw:12:4
# check: enum_rust_like/src/main.sw:12:3
# check: $()F(u32), // Illegal
# nextln: $()Expected ':'. Enum variants must be in the form `Variant: ()`, `Variant: <type>`, or `Variant: (<type1>, ..., <typeN>)`. E.g., `Foo: (), or `Bar: (bool, u32)`.
# nextln: $()`F(u32)` is not a valid enum variant declaration.
# nextln: $()Did you mean `F: (u32)`?
# check: $()In Sway, enum variants are in the form `Variant: ()`, `Variant: <type>`, or `Variant: (<type1>, ..., <typeN>)`.
# nextln: $()E.g., `Foo: (), `Bar: u64`, or `Bar: (bool, u32)`.

# check: $()error
# check: enum_rust_like/src/main.sw:17:3
# check: $()F(bool, u32, str[4]), // Illegal
# nextln: $()`F(bool, u32, str[4])` is not a valid enum variant declaration.
# nextln: $()Did you mean `F: (bool, u32, str[4])`?
# check: $()In Sway, enum variants are in the form `Variant: ()`, `Variant: <type>`, or `Variant: (<type1>, ..., <typeN>)`.
# nextln: $()E.g., `Foo: (), `Bar: u64`, or `Bar: (bool, u32)`.

# check: $()error
# check: enum_rust_like/src/main.sw:22:3
# check: $()A { x: i32, y: i32 }, // Illegal
# nextln: $()`A { x: i32, y: i32 }` is not a valid enum variant declaration.
# check: $()In Sway, enum variants are in the form `Variant: ()`, `Variant: <type>`, or `Variant: (<type1>, ..., <typeN>)`.
# nextln: $()E.g., `Foo: (), `Bar: u64`, or `Bar: (bool, u32)`.
Loading