Skip to content

Commit

Permalink
Add error for duplicate struct field initialization (#6401)
Browse files Browse the repository at this point in the history
## Description

Fixes #6378

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [x] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] If my change requires substantial documentation changes, I have
[requested support from the DevRel
team](https://github.com/FuelLabs/devrel-requests/issues/new/choose)
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.

Co-authored-by: Sophie Dankel <[email protected]>
  • Loading branch information
IGI-111 and sdankel authored Aug 7, 2024
1 parent d5aa2d6 commit a6f1ebe
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::BTreeSet;

use itertools::Itertools;
use sway_error::{
error::{CompileError, StructFieldUsageContext},
Expand Down Expand Up @@ -140,6 +142,18 @@ pub(crate) fn struct_instantiation(
});
}

// Check that there are no duplicate fields.
let mut seen_fields: BTreeSet<Ident> = BTreeSet::new();
for field in fields.iter() {
if let Some(duplicate) = seen_fields.get(&field.name) {
handler.emit_err(CompileError::StructFieldDuplicated {
field_name: field.name.clone(),
duplicate: duplicate.clone(),
});
}
seen_fields.insert(field.name.clone());
}

// Check that there are no extra fields.
for field in fields.iter() {
if !struct_fields.iter().any(|x| x.name == field.name) {
Expand Down
21 changes: 21 additions & 0 deletions sway-error/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,8 @@ pub enum CompileError {
struct_is_empty: bool,
usage_context: StructFieldUsageContext,
},
#[error("Field \"{field_name}\" has multiple definitions.")]
StructFieldDuplicated { field_name: Ident, duplicate: Ident },
#[error("No method named \"{method_name}\" found for type \"{type_name}\".")]
MethodNotFound {
method_name: Ident,
Expand Down Expand Up @@ -1071,6 +1073,7 @@ impl Spanned for CompileError {
StructCannotBeInstantiated { span, .. } => span.clone(),
StructFieldIsPrivate { field_name, .. } => field_name.span(),
StructFieldDoesNotExist { field_name, .. } => field_name.span(),
StructFieldDuplicated { field_name, .. } => field_name.span(),
MethodNotFound { span, .. } => span.clone(),
ModuleNotFound { span, .. } => span.clone(),
TupleElementAccessOnNonTuple { span, .. } => span.clone(),
Expand Down Expand Up @@ -2061,6 +2064,24 @@ impl ToDiagnostic for CompileError {
},
help: vec![],
},
StructFieldDuplicated { field_name, duplicate } => Diagnostic {
reason: Some(Reason::new(code(1), "Struct field has multiple definitions".to_string())),
issue: Issue::error(
source_engine,
field_name.span(),
format!("Field \"{field_name}\" has multiple definitions.")
),
hints: {
vec![
Hint::info(
source_engine,
duplicate.span(),
"Field definition duplicated here.".into(),
)
]
},
help: vec![],
},
NotIndexable { actually, span } => Diagnostic {
reason: Some(Reason::new(code(1), "Type is not indexable".to_string())),
issue: Issue::error(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[[package]]
name = "core"
source = "path+from-root-62266B0554A16849"

[[package]]
name = "duplicate_struct_field"
source = "member"
dependencies = ["core"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[project]
name = "duplicate_struct_field"
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
implicit-std = false

[dependencies]
core = { path = "../../../../../../sway-lib-core" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"configurables": [],
"functions": [
{
"attributes": null,
"inputs": [],
"name": "bar",
"output": {
"name": "",
"type": 0,
"typeArguments": null
}
},
{
"attributes": null,
"inputs": [],
"name": "baz",
"output": {
"name": "",
"type": 0,
"typeArguments": null
}
}
],
"loggedTypes": [],
"messagesTypes": [],
"types": [
{
"components": [],
"type": "()",
"typeId": 0,
"typeParameters": null
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
script;

struct S {
x: u64,
}

pub fn main() {
let _ = S{
x:1,
x: "a",
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
category = "fail"

# check: $()Struct field has multiple definitions
# check: $()Field "x" has multiple definitions.

0 comments on commit a6f1ebe

Please sign in to comment.