1use erg_common::dict::Dict;
2use erg_common::style::{Attribute, Color, StyledStrings, THEME};
3use erg_common::{option_enum_unwrap, switch_lang};
4
5use crate::ty::typaram::TyParam;
6use crate::ty::value::ValueObj;
7use crate::ty::{Field, HasType, Predicate, SubrKind, SubrType, Type};
8
9use crate::context::Context;
10
11const HINT: Color = THEME.colors.hint;
12const ERR: Color = THEME.colors.error;
13#[cfg(not(feature = "pretty"))]
14const ATTR: Attribute = Attribute::Bold;
15#[cfg(feature = "pretty")]
16const ATTR: Attribute = Attribute::Underline;
17
18#[derive(PartialEq, Eq)]
19enum Sequence {
20 Forward,
21 Backward,
22}
23
24impl Context {
26 fn get_verb_and_preposition(trait_: &Type) -> Option<(&str, &str, Sequence)> {
28 match &trait_.qual_name()[..] {
29 "Add" => Some(("add", "and", Sequence::Forward)),
30 "Sub" => Some(("subtract", "from", Sequence::Backward)),
31 "Mul" => Some(("multiply", "and", Sequence::Forward)),
32 "Div" => Some(("divide", "by", Sequence::Forward)),
33 "Eq" => Some(("compare", "and", Sequence::Forward)),
34 "Ord" => Some(("compare", "and", Sequence::Forward)),
35 _ => None,
36 }
37 }
38
39 pub(crate) fn get_call_type_mismatch_hint(
40 &self,
41 callee_t: &Type,
42 attr: Option<&str>,
43 nth: usize,
44 expected: &Type,
45 found: &Type,
46 ) -> Option<String> {
47 if self.cfg.fast_error_report {
48 return None;
49 }
50 if &callee_t.qual_name()[..] == "List" && attr == Some("__getitem__") && nth == 1 {
51 let len = &callee_t.typarams().get(1).cloned()?;
52 let (_, _, pred) = found.clone().deconstruct_refinement().ok()?;
53 if let Predicate::Equal { rhs: accessed, .. } = pred {
54 let accessed = if let TyParam::Value(value) = &accessed {
55 value
56 .clone()
57 .try_add(ValueObj::Nat(1))
58 .map(TyParam::Value)
59 .unwrap_or_else(|| accessed.clone())
60 } else {
61 accessed.clone()
62 };
63 return Some(switch_lang! {
64 "japanese" => format!("リストの長さは{len}ですが、{accessed}番目の要素にアクセスしようとしています"),
65 "simplified_chinese" => format!("数组长度为{len}但尝试访问第{accessed}个元素"),
66 "traditional_chinese" => format!("陣列長度為{len}但嘗試訪問第{accessed}個元素"),
67 "english" => format!("List length is {len} but tried to access the {accessed}th element"),
68 });
69 }
70 }
71 self.get_simple_type_mismatch_hint(expected, found)
72 }
73
74 pub(crate) fn get_simple_type_mismatch_hint(
75 &self,
76 expected: &Type,
77 found: &Type,
78 ) -> Option<String> {
79 if self.cfg.fast_error_report {
80 return None;
81 }
82 let expected = if let Some(fv) = expected.as_free() {
83 if fv.is_linked() {
84 fv.crack().clone()
85 } else {
86 let (_sub, sup) = fv.get_subsup()?;
87 sup
88 }
89 } else {
90 expected.clone()
91 };
92 let mut hint = StyledStrings::default();
93 match (&expected, &found) {
94 (Type::Subr(expt), Type::Subr(fnd)) => {
95 if let Some(hint) = self.get_subr_type_mismatch_hint(expt, fnd) {
96 return Some(hint);
97 }
98 }
99 (Type::Quantified(expt), Type::Subr(fnd)) => {
100 if let Type::Subr(expt) = expt.as_ref() {
101 if let Some(hint) = self.get_subr_type_mismatch_hint(expt, fnd) {
102 return Some(hint);
103 }
104 }
105 }
106 (Type::Quantified(expt), Type::Quantified(fnd)) => {
107 if let (Type::Subr(expt), Type::Subr(fnd)) = (expt.as_ref(), fnd.as_ref()) {
108 if let Some(hint) = self.get_subr_type_mismatch_hint(expt, fnd) {
109 return Some(hint);
110 }
111 }
112 }
113 (Type::Record(expt), Type::Record(fnd)) => {
114 if let Some(hint) = self.get_record_type_mismatch_hint(expt, fnd) {
115 return Some(hint);
116 }
117 }
118 (Type::NamedTuple(expt), Type::NamedTuple(fnd)) => {
119 let expt = Dict::from(expt.clone());
120 let fnd = Dict::from(fnd.clone());
121 if let Some(hint) = self.get_record_type_mismatch_hint(&expt, &fnd) {
122 return Some(hint);
123 }
124 }
125 (Type::And(tys, _), found) if tys.len() == 2 => {
126 let mut iter = tys.iter();
127 let l = iter.next().unwrap();
128 let r = iter.next().unwrap();
129 let left = self.readable_type(l.clone());
130 let right = self.readable_type(r.clone());
131 if self.supertype_of(l, found) {
132 let msg = switch_lang!(
133 "japanese" => format!("型{found}は{left}のサブタイプですが、{right}のサブタイプではありません"),
134 "simplified_chinese" => format!("类型{found}是{left}的子类型但不是{right}的子类型"),
135 "traditional_chinese" => format!("型別{found}是{left}的子型別但不是{right}的子型別"),
136 "english" => format!("Type {found} is a subtype of {left} but not of {right}"),
137 );
138 hint.push_str(&msg);
139 return Some(hint.to_string());
140 } else if self.supertype_of(r, found) {
141 let msg = switch_lang!(
142 "japanese" => format!("型{found}は{right}のサブタイプですが、{left}のサブタイプではありません"),
143 "simplified_chinese" => format!("类型{found}是{right}的子类型但不是{left}的子类型"),
144 "traditional_chinese" => format!("型別{found}是{right}的子型別但不是{left}の子型別"),
145 "english" =>format!("Type {found} is a subtype of {right} but not of {left}"),
146 );
147 hint.push_str(&msg);
148 return Some(hint.to_string());
149 }
150 }
151 _ => {}
152 }
153
154 match (&expected.qual_name()[..], &found.qual_name()[..]) {
155 ("Eq", "Float") => {
156 switch_lang!(
157 "japanese" => {
158 hint.push_str("Floatは等価関係が定義されていません。");
159 hint.push_str_with_color_and_attr("l == r", ERR, ATTR);
160 hint.push_str("ではなく、");
161 hint.push_str_with_color_and_attr("l - r <= Float.EPSILON", HINT, ATTR);
162 hint.push_str("あるいは");
163 hint.push_str_with_color_and_attr("l.nearly_eq(r)", HINT, ATTR);
164 hint.push_str("を使用してください");
165 },
166 "simplified_chinese" => {
167 hint.push_str("Float没有定义等价关系。你应该使用");
168 hint.push_str_with_color_and_attr("l - r <= Float.EPSILON", HINT, ATTR);
169 hint.push_str("或者");
170 hint.push_str_with_color_and_attr("l.nearly_eq(r)", HINT, ATTR);
171 hint.push_str("而不是");
172 hint.push_str_with_color_and_attr("l == r", ERR, ATTR);
173 },
174 "traditional_chinese" => {
175 hint.push_str("Float沒有定義等價關係。你應該使用");
176 hint.push_str_with_color_and_attr("l - r <= Float.EPSILON", HINT, ATTR);
177 hint.push_str("或者");
178 hint.push_str_with_color_and_attr("l.nearly_eq(r)", HINT, ATTR);
179 hint.push_str("而不是");
180 hint.push_str_with_color_and_attr("l == r", ERR, ATTR);
181 },
182 "english" => {
183 hint.push_str("Float has no equivalence relation defined. You should use ");
184 hint.push_str_with_color_and_attr("l - r <= Float.EPSILON", HINT, ATTR);
185 hint.push_str(" or ");
186 hint.push_str_with_color_and_attr("l.nearly_eq(r)", HINT, ATTR);
187 hint.push_str(" instead of ");
188 hint.push_str_with_color_and_attr("l == r", ERR, ATTR);
189 },
190 );
191 Some(hint.to_string())
192 }
193 _ => {
194 let (verb, preposition, _sequence) = Self::get_verb_and_preposition(&expected)?;
195 found
196 .union_pair()
197 .map(|(t1, t2)| format!("cannot {verb} {t1} {preposition} {t2}"))
198 .or_else(|| {
199 expected.inner_ts().first().map(|expected_inner| {
200 let expected_inner = self.readable_type(expected_inner.clone());
201 format!("cannot {verb} {found} {preposition} {expected_inner}")
202 })
203 })
204 }
205 }
206 }
207
208 fn get_record_type_mismatch_hint(
209 &self,
210 expected: &Dict<Field, Type>,
211 found: &Dict<Field, Type>,
212 ) -> Option<String> {
213 let missing = expected.clone().diff(found);
214 if missing.is_empty() {
215 let mut mismatched = "".to_string();
216 for (field, expected) in expected.iter() {
217 if let Some(found) = found.get(field) {
218 if !self.supertype_of(expected, found) {
219 if !mismatched.is_empty() {
220 mismatched.push_str(", ");
221 }
222 mismatched.push_str(&format!("{field}: {expected} but found {found}"));
223 }
224 }
225 }
226 if mismatched.is_empty() {
227 None
228 } else {
229 Some(mismatched)
230 }
231 } else {
232 let mut hint = "missing: ".to_string();
233 for (i, (field, typ)) in missing.iter().enumerate() {
234 if i > 0 {
235 hint.push_str(", ");
236 }
237 hint.push_str(&format!("{field}: {typ}"));
238 }
239 Some(hint)
240 }
241 }
242
243 fn get_subr_type_mismatch_hint(&self, expected: &SubrType, found: &SubrType) -> Option<String> {
245 let mut hint = StyledStrings::default();
246 if let (SubrKind::Func, SubrKind::Proc) = (expected.kind, found.kind) {
247 switch_lang!(
248 "japanese" => {
249 hint.push_str("この仮引数は(副作用のない)関数を受け取りますが、プロシージャは副作用があるため受け取りません。副作用を取り除き、");
250 hint.push_str_with_color_and_attr("=>", ERR, ATTR);
251 hint.push_str("の代わりに");
252 hint.push_str_with_color_and_attr("->", HINT, ATTR);
253 hint.push_str("を使用する必要があります");
254 },
255 "simplified_chinese" => {
256 hint.push_str("此参数接受函数(无副作用),但不接受过程,因为过程有副作用。你应该使用");
257 hint.push_str_with_color_and_attr("=>", HINT, ATTR);
258 hint.push_str("而不是");
259 hint.push_str_with_color_and_attr("->", ERR, ATTR);
260 },
261 "traditional_chinese" => {
262 hint.push_str("此參數接受函數(無副作用),但不接受過程,因為過程有副作用。你應該使用");
263 hint.push_str_with_color_and_attr("=>", HINT, ATTR);
264 hint.push_str("而不是");
265 hint.push_str_with_color_and_attr("->", ERR, ATTR);
266 },
267 "english" => {
268 hint.push_str("This param accepts func (without side-effects) but not proc because of side-effects. You should use ");
269 hint.push_str_with_color_and_attr("=>", HINT, ATTR);
270 hint.push_str(" instead of ");
271 hint.push_str_with_color_and_attr("->", ERR, ATTR);
272 },
273 );
274 return Some(hint.to_string());
275 }
276 if let Some((expect, _found)) = expected
277 .non_var_params()
278 .zip(found.non_var_params())
279 .find(|(expect, found)| expect.typ().is_ref() && !found.typ().is_ref())
280 {
281 let hint = switch_lang!(
282 "japanese" => format!("{expect}は参照を受け取るよう宣言されましたが、実体が渡されています(refプレフィックスを追加してください)"),
283 "simplified_chinese" => format!("{expect}被声明为接受引用,但实体被传递(请添加ref前缀)"),
284 "traditional_chinese" => format!("{expect}被宣告為接受引用,但實體被傳遞(請添加ref前綴)"),
285 "english" => format!("{expect} is declared as a reference parameter but definition is an owned parameter (add `ref` prefix)"),
286 );
287 return Some(hint);
288 }
289 None
290 }
291
292 pub(crate) fn get_no_candidate_hint(&self, proj: &Type) -> Option<String> {
293 if self.cfg.fast_error_report {
294 return None;
295 }
296 match proj {
297 Type::Proj { lhs, rhs: _ } => {
298 if let Some(fv) = lhs.as_free() {
299 let (sub, sup) = fv.get_subsup()?;
300 let (verb, preposition, sequence) = Self::get_verb_and_preposition(&sup)?;
301 let sup = *option_enum_unwrap!(sup.typarams().first()?.clone(), TyParam::Type)?;
302 let sup = self.readable_type(sup);
303 let (l, r) = if sequence == Sequence::Forward {
304 (sub, sup)
305 } else {
306 (sup, sub)
307 };
308 Some(format!("cannot {verb} {l} {preposition} {r}"))
309 } else {
310 None
311 }
312 }
313 _ => None,
314 }
315 }
316}