diff --git a/schala-lang/language/src/symbol_table/mod.rs b/schala-lang/language/src/symbol_table/mod.rs index 0310f01..e8d024f 100644 --- a/schala-lang/language/src/symbol_table/mod.rs +++ b/schala-lang/language/src/symbol_table/mod.rs @@ -1,5 +1,4 @@ -use std::collections::HashMap; -use std::collections::hash_map::Entry; +use std::collections::{HashMap, hash_map::Entry}; use std::rc::Rc; use std::fmt; @@ -58,6 +57,11 @@ pub enum SymbolError { DuplicateName { prev_name: FQSN, location: Location + }, + DuplicateRecord { + type_name: FQSN, + location: Location, + member: String, } } @@ -301,6 +305,7 @@ impl SymbolTable { } fn add_type_members(&mut self, type_name: &TypeSingletonName, type_body: &TypeBody, _mutable: &bool, location: Location, scope_stack: &mut Vec) -> Vec { + let mut member_errors = vec![]; let mut errors = vec![]; let mut register = |fqsn: FQSN, spec: SymbolSpec| { @@ -343,6 +348,23 @@ impl SymbolTable { }, Variant::Record { name, members } => { let fq_name = FQSN::from_scope_stack(scope_stack.as_ref(), name.clone()); + + let mut seen_members = HashMap::new(); + for (member_name, _) in members.iter() { + match seen_members.entry(member_name.as_ref()) { + Entry::Occupied(o) => { + let location = *o.get(); + member_errors.push(SymbolError::DuplicateRecord { + type_name: fq_name.clone(), + location, + member: member_name.as_ref().to_string(), + }); + }, + //TODO eventually this should track meaningful locations + Entry::Vacant(v) => { v.insert(Location::default()); } + } + } + let spec = SymbolSpec::RecordConstructor { index, type_name: name.clone(), @@ -350,30 +372,12 @@ impl SymbolTable { .map(|(_, _)| (Rc::new("DUMMY_FIELD".to_string()), Rc::new("DUMMY_TYPE_ID".to_string()))).collect() }; register(fq_name, spec); - //TODO check for duplicates among struct member definitions - /* - - let mut duplicate_member_definitions = Vec::new(); - for (member_name, member_type) in defined_members { - match members.entry(member_name.clone()) { - Entry::Occupied(_) => duplicate_member_definitions.push(member_name.clone()), - Entry::Vacant(v) => { - v.insert(match member_type { - TypeIdentifier::Singleton(TypeSingletonName { name, ..}) => name.clone(), - TypeIdentifier::Tuple(_) => unimplemented!(), - }); - } - } - } - if duplicate_member_definitions.len() != 0 { - return Err(format!("Duplicate member(s) in definition of type {}: {:?}", type_name, duplicate_member_definitions)); - } - */ } } } scope_stack.pop(); + errors.extend(member_errors.into_iter()); errors } } diff --git a/schala-lang/language/src/symbol_table/test.rs b/schala-lang/language/src/symbol_table/test.rs index 28a3843..c815912 100644 --- a/schala-lang/language/src/symbol_table/test.rs +++ b/schala-lang/language/src/symbol_table/test.rs @@ -206,3 +206,28 @@ fn duplicate_modules() { ); } + +#[test] +fn duplicate_struct_members() { + //TODO this is a parser error + /* + let source = r#" + type Tarak = Tarak { + loujet: i32, + mets: i32, + mets: i32, + } + "#; + */ + + let source = r#" type Tarak = Tarak { loujet: i32, mets: i32, mets: i32 } "#; + + let (_, output) = add_symbols(source); + let errs = output.unwrap_err(); + assert_matches!(&errs[..], [ + SymbolError::DuplicateRecord { + type_name, member, ..}, + ] if type_name == &FQSN::from_strs(&["Tarak", "Tarak"]) && member == "mets" + ); + +}