|
1 | 1 | use std::collections::{HashMap, HashSet}; |
2 | 2 |
|
| 3 | +use crate::diagnostic::Span; |
3 | 4 | use crate::syntax::ast::{ |
4 | 5 | DataEffect, EffectDecl, FieldDecl, FunctionDecl, Item, Param, Program as SyntaxProgram, |
5 | 6 | TypeDecl, TypeKind, |
@@ -61,21 +62,64 @@ pub struct TypeInfo { |
61 | 62 | pub fields: HashMap<String, FieldInfo>, |
62 | 63 | } |
63 | 64 |
|
| 65 | +#[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 66 | +pub enum DuplicateSymbolKind { |
| 67 | + Function, |
| 68 | + Type, |
| 69 | + Constructor, |
| 70 | +} |
| 71 | + |
| 72 | +#[derive(Debug, Clone, PartialEq, Eq)] |
| 73 | +pub struct DuplicateSymbol { |
| 74 | + pub kind: DuplicateSymbolKind, |
| 75 | + pub name: String, |
| 76 | + pub first_span: Span, |
| 77 | + pub duplicate_span: Span, |
| 78 | +} |
| 79 | + |
64 | 80 | #[derive(Debug, Default)] |
65 | 81 | pub struct Hir { |
66 | 82 | signatures: HashMap<String, FunctionSig>, |
67 | 83 | types: HashMap<String, TypeInfo>, |
68 | 84 | fields_by_name: HashMap<String, Vec<FieldInfo>>, |
| 85 | + duplicate_symbols: Vec<DuplicateSymbol>, |
69 | 86 | } |
70 | 87 |
|
71 | 88 | impl Hir { |
72 | 89 | pub fn from_syntax(program: &SyntaxProgram) -> Self { |
73 | 90 | let mut hir = Self::default(); |
74 | 91 | hir.insert_builtins(); |
| 92 | + let mut type_symbols = HashMap::new(); |
| 93 | + let mut callable_symbols = HashMap::new(); |
75 | 94 | for item in &program.items { |
76 | 95 | match item { |
77 | | - Item::Function(function) => hir.insert_function(function_sig_from_decl(function)), |
78 | | - Item::Type(type_decl) => hir.insert_type(type_info_from_decl(type_decl)), |
| 96 | + Item::Function(function) => { |
| 97 | + record_duplicate_symbol( |
| 98 | + &mut hir.duplicate_symbols, |
| 99 | + &mut callable_symbols, |
| 100 | + DuplicateSymbolKind::Function, |
| 101 | + &function.name, |
| 102 | + &function.span, |
| 103 | + ); |
| 104 | + hir.insert_function(function_sig_from_decl(function)); |
| 105 | + } |
| 106 | + Item::Type(type_decl) => { |
| 107 | + record_duplicate_symbol( |
| 108 | + &mut hir.duplicate_symbols, |
| 109 | + &mut type_symbols, |
| 110 | + DuplicateSymbolKind::Type, |
| 111 | + &type_decl.name, |
| 112 | + &type_decl.span, |
| 113 | + ); |
| 114 | + record_duplicate_symbol( |
| 115 | + &mut hir.duplicate_symbols, |
| 116 | + &mut callable_symbols, |
| 117 | + DuplicateSymbolKind::Constructor, |
| 118 | + &type_decl.name, |
| 119 | + &type_decl.span, |
| 120 | + ); |
| 121 | + hir.insert_type(type_info_from_decl(type_decl)); |
| 122 | + } |
79 | 123 | } |
80 | 124 | } |
81 | 125 | hir |
@@ -109,6 +153,10 @@ impl Hir { |
109 | 153 | self.fields_named(field_name).any(|field| field.is_handle) |
110 | 154 | } |
111 | 155 |
|
| 156 | + pub fn duplicate_symbols(&self) -> &[DuplicateSymbol] { |
| 157 | + &self.duplicate_symbols |
| 158 | + } |
| 159 | + |
112 | 160 | fn insert_function(&mut self, signature: FunctionSig) { |
113 | 161 | let key = match &signature.namespace { |
114 | 162 | Some(namespace) => qualified_key(namespace, &signature.name), |
@@ -155,6 +203,39 @@ fn function_sig_from_decl(function: &FunctionDecl) -> FunctionSig { |
155 | 203 | } |
156 | 204 | } |
157 | 205 |
|
| 206 | +fn record_duplicate_symbol( |
| 207 | + duplicates: &mut Vec<DuplicateSymbol>, |
| 208 | + symbols: &mut HashMap<String, (DuplicateSymbolKind, Span)>, |
| 209 | + kind: DuplicateSymbolKind, |
| 210 | + name: &str, |
| 211 | + span: &Span, |
| 212 | +) { |
| 213 | + if let Some((first_kind, first_span)) = symbols.get(name) { |
| 214 | + duplicates.push(DuplicateSymbol { |
| 215 | + kind: duplicate_symbol_kind(*first_kind, kind), |
| 216 | + name: name.to_string(), |
| 217 | + first_span: first_span.clone(), |
| 218 | + duplicate_span: span.clone(), |
| 219 | + }); |
| 220 | + return; |
| 221 | + } |
| 222 | + |
| 223 | + symbols.insert(name.to_string(), (kind, span.clone())); |
| 224 | +} |
| 225 | + |
| 226 | +fn duplicate_symbol_kind( |
| 227 | + first: DuplicateSymbolKind, |
| 228 | + duplicate: DuplicateSymbolKind, |
| 229 | +) -> DuplicateSymbolKind { |
| 230 | + match (first, duplicate) { |
| 231 | + (DuplicateSymbolKind::Function, DuplicateSymbolKind::Function) => { |
| 232 | + DuplicateSymbolKind::Function |
| 233 | + } |
| 234 | + (DuplicateSymbolKind::Type, DuplicateSymbolKind::Type) => DuplicateSymbolKind::Type, |
| 235 | + _ => DuplicateSymbolKind::Constructor, |
| 236 | + } |
| 237 | +} |
| 238 | + |
158 | 239 | fn param_sig_from_decl(param: &Param) -> ParamSig { |
159 | 240 | ParamSig { |
160 | 241 | name: param.name.clone(), |
@@ -679,4 +760,30 @@ fn cache_put(cache: mut Cache, value: read Image) -> Unit |
679 | 760 | .expect("builtin signature exists"); |
680 | 761 | assert_eq!(load.return_type.as_deref(), Some("Image")); |
681 | 762 | } |
| 763 | + |
| 764 | + #[test] |
| 765 | + fn records_duplicate_callable_symbols() { |
| 766 | + let source = r#" |
| 767 | +mode: managed |
| 768 | +
|
| 769 | +struct Image { |
| 770 | + pixels: Buffer |
| 771 | +} |
| 772 | +
|
| 773 | +fn Image(path: read Path) -> Image { |
| 774 | +} |
| 775 | +"#; |
| 776 | + |
| 777 | + let program = parse_source("test.rss", source); |
| 778 | + let hir = Hir::from_syntax(&program); |
| 779 | + let duplicate = hir |
| 780 | + .duplicate_symbols() |
| 781 | + .first() |
| 782 | + .expect("constructor/function duplicate is recorded"); |
| 783 | + |
| 784 | + assert_eq!(duplicate.kind, DuplicateSymbolKind::Constructor); |
| 785 | + assert_eq!(duplicate.name, "Image"); |
| 786 | + assert_eq!(duplicate.first_span.line, 4); |
| 787 | + assert_eq!(duplicate.duplicate_span.line, 8); |
| 788 | + } |
682 | 789 | } |
0 commit comments