1use std::fmt;
2use std::path::Path;
3
4use erg_common::error::Location;
5use erg_common::pathutil::NormalizedPathBuf;
6use erg_common::set::Set;
7use erg_common::traits::Immutable;
8use erg_common::{switch_lang, Str};
9
10use erg_parser::ast::DefId;
11
12use crate::context::{ContextKind, DefaultInfo};
13use crate::ty::{Field, HasType, Type, Visibility};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16#[repr(u8)]
17pub enum Mutability {
18 Immutable,
19 Const,
20}
21
22impl From<&str> for Mutability {
23 fn from(item: &str) -> Self {
24 if item.chars().next().is_some_and(|c| c.is_uppercase()) {
25 Self::Const
26 } else {
27 Self::Immutable
28 }
29 }
30}
31
32impl Mutability {
33 pub const fn is_const(&self) -> bool {
34 matches!(self, Self::Const)
35 }
36}
37
38use Mutability::*;
39
40#[derive(Debug, Clone, PartialEq, Eq, Hash)]
41pub enum VarKind {
42 Defined(DefId),
43 Declared,
44 InstanceAttr,
45 Parameter {
46 def_id: DefId,
47 var: bool,
48 default: DefaultInfo,
49 },
50 Auto,
51 FixedAuto,
52 DoesNotExist,
53 Builtin,
54}
55
56impl VarKind {
57 pub const fn parameter(def_id: DefId, var: bool, default: DefaultInfo) -> Self {
58 Self::Parameter {
59 def_id,
60 var,
61 default,
62 }
63 }
64
65 pub const fn nd_parameter(def_id: DefId) -> Self {
66 Self::parameter(def_id, false, DefaultInfo::NonDefault)
67 }
68
69 pub const fn has_default(&self) -> bool {
70 match self {
71 Self::Parameter { default, .. } => default.has_default(),
72 _ => false,
73 }
74 }
75
76 pub const fn is_parameter(&self) -> bool {
77 matches!(self, Self::Parameter { .. })
78 }
79
80 pub const fn is_var_params(&self) -> bool {
81 match self {
82 Self::Parameter { var, .. } => *var,
83 _ => false,
84 }
85 }
86
87 pub const fn is_defined(&self) -> bool {
88 matches!(self, Self::Defined(_))
89 }
90
91 pub const fn can_capture(&self) -> bool {
92 matches!(
93 self,
94 Self::Defined(_) | Self::Declared | Self::Parameter { .. }
95 )
96 }
97
98 pub const fn does_not_exist(&self) -> bool {
99 matches!(self, Self::DoesNotExist)
100 }
101
102 pub const fn is_builtin(&self) -> bool {
103 matches!(self, Self::Builtin)
104 }
105
106 pub const fn is_auto(&self) -> bool {
107 matches!(self, Self::Auto)
108 }
109
110 pub const fn is_instance_attr(&self) -> bool {
111 matches!(self, Self::InstanceAttr)
112 }
113
114 pub const fn display(&self) -> &'static str {
115 match self {
116 Self::Auto | Self::FixedAuto => switch_lang!(
117 "japanese" => "自動",
118 "simplified_chinese" => "自动",
119 "traditional_chinese" => "自動",
120 "english" => "auto",
121 ),
122 Self::Builtin => switch_lang!(
123 "japanese" => "組み込み",
124 "simplified_chinese" => "内置",
125 "traditional_chinese" => "內置",
126 "english" => "builtin",
127 ),
128 Self::InstanceAttr => switch_lang!(
129 "japanese" => "インスタンス",
130 "simplified_chinese" => "实例",
131 "traditional_chinese" => "實例",
132 "english" => "instance",
133 ),
134 _ => "",
135 }
136 }
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, Hash)]
140pub struct AbsLocation {
141 pub module: Option<NormalizedPathBuf>,
142 pub loc: Location,
143}
144
145impl Immutable for AbsLocation {}
146
147impl fmt::Display for AbsLocation {
148 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149 if let Some(module) = &self.module {
150 write!(f, "{}@{}", module.display(), self.loc)
151 } else {
152 write!(f, "?@{}", self.loc)
153 }
154 }
155}
156
157impl std::str::FromStr for AbsLocation {
158 type Err = ();
159
160 fn from_str(s: &str) -> Result<Self, Self::Err> {
161 let mut split = s.split('@');
162 let module = split.next().map(NormalizedPathBuf::from);
163 let loc = split.next().ok_or(())?.parse().map_err(|_| ())?;
164 Ok(Self { module, loc })
165 }
166}
167
168impl AbsLocation {
169 pub const fn new(module: Option<NormalizedPathBuf>, loc: Location) -> Self {
170 Self { module, loc }
171 }
172
173 pub const fn unknown() -> Self {
174 Self::new(None, Location::Unknown)
175 }
176
177 pub fn is_unknown(&self) -> bool {
178 self.module.is_none() && self.loc.is_unknown()
179 }
180
181 pub fn is_real(&self) -> bool {
182 self.module.is_some() && self.loc.is_real()
183 }
184
185 pub fn starts_with(&self, path: impl AsRef<Path>) -> bool {
186 self.module.as_ref().is_some_and(|p| p.starts_with(path))
187 }
188
189 pub fn code(&self) -> Option<String> {
190 use std::io::{BufRead, BufReader};
191 self.module.as_ref().and_then(|module| {
192 let file = std::fs::File::open(module).ok()?;
193 let reader = BufReader::new(file);
194 reader
195 .lines()
196 .nth(
197 self.loc
198 .ln_begin()
199 .map(|l| l.saturating_sub(1))
200 .unwrap_or(0) as usize,
201 )
202 .and_then(|res| {
203 let res = res.ok()?;
204 let begin = self.loc.col_begin().unwrap_or(0) as usize;
205 let end = self.loc.col_end().unwrap_or(0) as usize;
206 if begin > res.len() || end > res.len() || begin > end {
207 return None;
208 }
209 let end = end.min(res.len());
210 let res = res[begin..end].to_string();
211 Some(res)
212 })
213 })
214 }
215}
216
217#[derive(Debug, Clone, PartialEq, Eq, Hash)]
218pub struct AliasInfo {
219 pub name: Str,
220 pub loc: AbsLocation,
221}
222
223impl AliasInfo {
224 pub const fn new(name: Str, loc: AbsLocation) -> Self {
225 Self { name, loc }
226 }
227}
228
229#[derive(Debug, Clone, PartialEq, Eq, Hash)]
231pub struct VarInfo {
232 pub t: Type,
233 pub muty: Mutability,
234 pub vis: Visibility,
235 pub kind: VarKind,
236 pub comptime_decos: Option<Set<Str>>,
237 pub ctx: ContextKind,
238 pub py_name: Option<Str>,
239 pub def_loc: AbsLocation,
240 pub alias_of: Option<Box<AliasInfo>>,
241}
242
243impl fmt::Display for VarInfo {
244 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
245 write!(
246 f,
247 "VarInfo{{t: {}, muty: {:?}, vis: {:?}, kind: {:?}, py_name: {:?}, def_loc: {} }}",
248 self.t, self.muty, self.vis, self.kind, self.py_name, self.def_loc
249 )
250 }
251}
252
253impl HasType for VarInfo {
254 #[inline]
255 fn ref_t(&self) -> &Type {
256 &self.t
257 }
258 #[inline]
259 fn ref_mut_t(&mut self) -> Option<&mut Type> {
260 Some(&mut self.t)
261 }
262 #[inline]
263 fn signature_t(&self) -> Option<&Type> {
264 None
265 }
266 #[inline]
267 fn signature_mut_t(&mut self) -> Option<&mut Type> {
268 None
269 }
270}
271
272impl Default for VarInfo {
273 fn default() -> Self {
274 Self::const_default_private()
275 }
276}
277
278impl VarInfo {
279 pub const ILLEGAL: Self = Self::const_default_private();
280
281 pub const fn const_default_private() -> Self {
282 Self::new(
283 Type::Failure,
284 Immutable,
285 Visibility::DUMMY_PRIVATE,
286 VarKind::DoesNotExist,
287 None,
288 ContextKind::Dummy,
289 None,
290 AbsLocation::unknown(),
291 )
292 }
293
294 pub const fn const_default_public() -> Self {
295 Self::new(
296 Type::Failure,
297 Immutable,
298 Visibility::DUMMY_PUBLIC,
299 VarKind::DoesNotExist,
300 None,
301 ContextKind::Dummy,
302 None,
303 AbsLocation::unknown(),
304 )
305 }
306
307 #[allow(clippy::too_many_arguments)]
308 pub const fn new(
309 t: Type,
310 muty: Mutability,
311 vis: Visibility,
312 kind: VarKind,
313 comptime_decos: Option<Set<Str>>,
314 ctx: ContextKind,
315 py_name: Option<Str>,
316 def_loc: AbsLocation,
317 ) -> Self {
318 Self {
319 t,
320 muty,
321 vis,
322 kind,
323 comptime_decos,
324 ctx,
325 py_name,
326 def_loc,
327 alias_of: None,
328 }
329 }
330
331 #[allow(clippy::too_many_arguments)]
332 pub fn maybe_alias(
333 t: Type,
334 muty: Mutability,
335 vis: Visibility,
336 kind: VarKind,
337 comptime_decos: Option<Set<Str>>,
338 ctx: ContextKind,
339 py_name: Option<Str>,
340 def_loc: AbsLocation,
341 alias_of: Option<AliasInfo>,
342 ) -> Self {
343 Self {
344 t,
345 muty,
346 vis,
347 kind,
348 comptime_decos,
349 ctx,
350 py_name,
351 def_loc,
352 alias_of: alias_of.map(Box::new),
353 }
354 }
355
356 pub fn same_id_as(&self, id: DefId) -> bool {
357 match self.kind {
358 VarKind::Defined(i) | VarKind::Parameter { def_id: i, .. } => id == i,
359 _ => false,
360 }
361 }
362
363 pub fn nd_parameter(t: Type, def_loc: AbsLocation, namespace: Str) -> Self {
364 let kind = VarKind::Parameter {
365 def_id: DefId(0),
366 var: false,
367 default: DefaultInfo::NonDefault,
368 };
369 Self::new(
370 t,
371 Immutable,
372 Visibility::private(namespace),
373 kind,
374 None,
375 ContextKind::Func,
376 None,
377 def_loc,
378 )
379 }
380
381 pub fn d_parameter(t: Type, def_loc: AbsLocation, namespace: Str) -> Self {
382 let kind = VarKind::Parameter {
383 def_id: DefId(0),
384 var: false,
385 default: DefaultInfo::WithDefault,
386 };
387 Self::new(
388 t,
389 Immutable,
390 Visibility::private(namespace),
391 kind,
392 None,
393 ContextKind::Func,
394 None,
395 def_loc,
396 )
397 }
398
399 pub fn instance_attr(
400 field: Field,
401 t: Type,
402 kind: ContextKind,
403 namespace: Str,
404 loc: AbsLocation,
405 ) -> Self {
406 let muty = if field.is_const() {
407 Mutability::Const
408 } else {
409 Mutability::Immutable
410 };
411 Self::new(
412 t,
413 muty,
414 Visibility::new(field.vis, namespace),
415 VarKind::InstanceAttr,
416 None,
417 kind,
418 None,
419 loc,
420 )
421 }
422
423 pub fn type_var(t: Type, def_loc: AbsLocation, namespace: Str) -> Self {
424 Self::new(
425 t,
426 Const,
427 Visibility::private(namespace),
428 VarKind::Declared,
429 None,
430 ContextKind::Dummy,
431 None,
432 def_loc,
433 )
434 }
435
436 pub fn record_field(t: Type, def_loc: AbsLocation, vis: Visibility) -> Self {
437 Self::new(
438 t,
439 Immutable,
440 vis,
441 VarKind::Declared,
442 None,
443 ContextKind::Dummy,
444 None,
445 def_loc,
446 )
447 }
448
449 pub fn is_ambiguously_typed_parameter(&self) -> bool {
450 self.kind.is_parameter()
451 && self
452 .t
453 .get_super()
454 .is_some_and(|sup| sup == Type::Obj || sup.is_structural())
455 }
456
457 pub const fn is_parameter(&self) -> bool {
458 self.kind.is_parameter()
459 }
460
461 pub fn impl_of(&self) -> Option<&Type> {
462 match &self.ctx {
463 ContextKind::MethodDefs(ty) => ty.as_ref(),
464 _ => None,
465 }
466 }
467
468 pub fn def_namespace(&self) -> &Str {
469 &self.vis.def_namespace
470 }
471
472 pub fn is_toplevel(&self) -> bool {
473 let ns = Str::rc(self.vis.def_namespace.trim_start_matches("./"));
474 ns.split_with(&[".", "::"]).len() == 1
475 }
476
477 pub fn is_fast_value(&self) -> bool {
478 !self.is_toplevel()
479 && !self.is_parameter()
480 && self.ctx.control_kind().is_none()
481 && (self.ctx.is_subr() || self.ctx.is_instant())
482 }
483}