1pub(crate) use self::diagnostics::LowerDiagnostic;
4use crate::resolve::{HasResolver, TypeNs};
5use crate::ty::{Substitution, TyKind};
6use crate::{
7 arena::map::ArenaMap,
8 code_model::StructKind,
9 diagnostics::DiagnosticSink,
10 name_resolution::Namespace,
11 primitive_type::PrimitiveType,
12 resolve::Resolver,
13 ty::{FnSig, Ty},
14 type_ref::{LocalTypeRefId, TypeRef, TypeRefMap, TypeRefSourceMap},
15 FileId, Function, HirDatabase, ModuleDef, Path, Struct, TypeAlias,
16};
17use crate::{HasVisibility, Visibility};
18use std::{ops::Index, sync::Arc};
19
20#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct LowerTyMap {
23 pub(crate) type_ref_to_type: ArenaMap<LocalTypeRefId, Ty>,
24 pub(crate) diagnostics: Vec<LowerDiagnostic>,
25
26 unknown_ty: Ty,
27}
28
29impl Default for LowerTyMap {
30 fn default() -> Self {
31 LowerTyMap {
32 type_ref_to_type: Default::default(),
33 diagnostics: vec![],
34 unknown_ty: TyKind::Unknown.intern(),
35 }
36 }
37}
38
39impl Index<LocalTypeRefId> for LowerTyMap {
40 type Output = Ty;
41 fn index(&self, expr: LocalTypeRefId) -> &Ty {
42 self.type_ref_to_type.get(expr).unwrap_or(&self.unknown_ty)
43 }
44}
45
46impl LowerTyMap {
47 pub(crate) fn add_diagnostics(
49 &self,
50 db: &dyn HirDatabase,
51 file_id: FileId,
52 source_map: &TypeRefSourceMap,
53 sink: &mut DiagnosticSink,
54 ) {
55 self.diagnostics
56 .iter()
57 .for_each(|it| it.add_to(db, file_id, source_map, sink))
58 }
59}
60
61impl Ty {
62 pub(crate) fn from_hir(
65 db: &dyn HirDatabase,
66 resolver: &Resolver,
67 type_ref_map: &TypeRefMap,
68 type_ref: LocalTypeRefId,
69 ) -> (Ty, Vec<diagnostics::LowerDiagnostic>) {
70 let mut diagnostics = Vec::new();
71 let ty =
72 Ty::from_hir_with_diagnostics(db, resolver, type_ref_map, &mut diagnostics, type_ref);
73 (ty, diagnostics)
74 }
75
76 fn from_hir_with_diagnostics(
79 db: &dyn HirDatabase,
80 resolver: &Resolver,
81 type_ref_map: &TypeRefMap,
82 diagnostics: &mut Vec<LowerDiagnostic>,
83 type_ref: LocalTypeRefId,
84 ) -> Ty {
85 let res = match &type_ref_map[type_ref] {
86 TypeRef::Path(path) => Ty::from_path(db, resolver, type_ref, path, diagnostics),
87 TypeRef::Error => Some((TyKind::Unknown.intern(), false)),
88 TypeRef::Tuple(inner) => {
89 let inner_tys = inner.iter().map(|tr| {
90 Self::from_hir_with_diagnostics(db, resolver, type_ref_map, diagnostics, *tr)
91 });
92 Some((
93 TyKind::Tuple(inner_tys.len(), inner_tys.collect()).intern(),
94 false,
95 ))
96 }
97 TypeRef::Never => Some((TyKind::Never.intern(), false)),
98 TypeRef::Array(inner) => {
99 let inner = Self::from_hir_with_diagnostics(
100 db,
101 resolver,
102 type_ref_map,
103 diagnostics,
104 *inner,
105 );
106 Some((TyKind::Array(inner).intern(), false))
107 }
108 };
109 if let Some((ty, is_cyclic)) = res {
110 if is_cyclic {
111 diagnostics.push(LowerDiagnostic::CyclicType { id: type_ref })
112 }
113 ty
114 } else {
115 diagnostics.push(LowerDiagnostic::UnresolvedType { id: type_ref });
116 TyKind::Unknown.intern()
117 }
118 }
119
120 fn from_path(
122 db: &dyn HirDatabase,
123 resolver: &Resolver,
124 type_ref: LocalTypeRefId,
125 path: &Path,
126 diagnostics: &mut Vec<LowerDiagnostic>,
127 ) -> Option<(Self, bool)> {
128 let (ty, vis) = resolver.resolve_path_as_type_fully(db.upcast(), path)?;
130
131 let def = match ty {
133 TypeNs::StructId(id) => TypableDef::Struct(id.into()),
134 TypeNs::TypeAliasId(id) => TypableDef::TypeAlias(id.into()),
135 TypeNs::PrimitiveType(id) => TypableDef::PrimitiveType(id),
136 };
137
138 if let Some(module) = resolver.module() {
140 if !vis.is_visible_from(db, module) {
141 diagnostics.push(LowerDiagnostic::TypeIsPrivate { id: type_ref })
142 }
143 }
144
145 Some(db.type_for_def(def, Namespace::Types))
146 }
147}
148
149pub fn types_from_hir(
151 db: &dyn HirDatabase,
152 resolver: &Resolver,
153 type_ref_map: &TypeRefMap,
154) -> Arc<LowerTyMap> {
155 let mut result = LowerTyMap::default();
156 for (id, _) in type_ref_map.iter() {
157 let ty =
158 Ty::from_hir_with_diagnostics(db, resolver, type_ref_map, &mut result.diagnostics, id);
159 result.type_ref_to_type.insert(id, ty);
160 }
161 Arc::new(result)
162}
163
164pub fn lower_struct_query(db: &dyn HirDatabase, s: Struct) -> Arc<LowerTyMap> {
165 let data = s.data(db.upcast());
166 types_from_hir(db, &s.id.resolver(db.upcast()), data.type_ref_map())
167}
168
169pub fn lower_type_alias_query(db: &dyn HirDatabase, t: TypeAlias) -> Arc<LowerTyMap> {
170 let data = t.data(db.upcast());
171 types_from_hir(db, &t.id.resolver(db.upcast()), data.type_ref_map())
172}
173
174#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
175pub enum TypableDef {
176 Function(Function),
177 PrimitiveType(PrimitiveType),
178 Struct(Struct),
179 TypeAlias(TypeAlias),
180}
181
182impl From<Function> for TypableDef {
183 fn from(f: Function) -> Self {
184 TypableDef::Function(f)
185 }
186}
187
188impl From<PrimitiveType> for TypableDef {
189 fn from(f: PrimitiveType) -> Self {
190 TypableDef::PrimitiveType(f)
191 }
192}
193
194impl From<Struct> for TypableDef {
195 fn from(f: Struct) -> Self {
196 TypableDef::Struct(f)
197 }
198}
199
200impl From<ModuleDef> for Option<TypableDef> {
201 fn from(d: ModuleDef) -> Self {
202 match d {
203 ModuleDef::Function(f) => Some(TypableDef::Function(f)),
204 ModuleDef::PrimitiveType(t) => Some(TypableDef::PrimitiveType(t)),
205 ModuleDef::Struct(t) => Some(TypableDef::Struct(t)),
206 ModuleDef::TypeAlias(t) => Some(TypableDef::TypeAlias(t)),
207 ModuleDef::Module(_) => None,
208 }
209 }
210}
211
212#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
213pub enum CallableDef {
214 Function(Function),
215 Struct(Struct),
216}
217impl_froms!(CallableDef: Function, Struct);
218
219impl CallableDef {
220 pub fn is_function(self) -> bool {
221 matches!(self, CallableDef::Function(_))
222 }
223
224 pub fn is_struct(self) -> bool {
225 matches!(self, CallableDef::Struct(_))
226 }
227}
228
229impl HasVisibility for CallableDef {
230 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
231 match self {
232 CallableDef::Struct(strukt) => strukt.visibility(db),
233 CallableDef::Function(function) => function.visibility(db),
234 }
235 }
236}
237
238pub(crate) fn type_for_def(db: &dyn HirDatabase, def: TypableDef, ns: Namespace) -> (Ty, bool) {
243 let ty = match (def, ns) {
244 (TypableDef::Function(f), Namespace::Values) => type_for_fn(db, f),
245 (TypableDef::PrimitiveType(t), Namespace::Types) => type_for_primitive(t),
246 (TypableDef::Struct(s), Namespace::Values) => type_for_struct_constructor(db, s),
247 (TypableDef::Struct(s), Namespace::Types) => type_for_struct(db, s),
248 (TypableDef::TypeAlias(t), Namespace::Types) => type_for_type_alias(db, t),
249
250 (TypableDef::Function(_), Namespace::Types) => TyKind::Unknown.intern(),
252 (TypableDef::PrimitiveType(_), Namespace::Values) => TyKind::Unknown.intern(),
253 (TypableDef::TypeAlias(_), Namespace::Values) => TyKind::Unknown.intern(),
254 };
255 (ty, false)
256}
257
258pub(crate) fn type_for_cycle_recover(
260 _db: &dyn HirDatabase,
261 _cycle: &[String],
262 _def: &TypableDef,
263 _ns: &Namespace,
264) -> (Ty, bool) {
265 (TyKind::Unknown.intern(), true)
266}
267
268fn type_for_primitive(def: PrimitiveType) -> Ty {
270 match def {
271 PrimitiveType::Float(f) => TyKind::Float(f.into()),
272 PrimitiveType::Int(i) => TyKind::Int(i.into()),
273 PrimitiveType::Bool => TyKind::Bool,
274 }
275 .intern()
276}
277
278fn type_for_fn(_db: &dyn HirDatabase, def: Function) -> Ty {
281 TyKind::FnDef(def.into(), Substitution::empty()).intern()
282}
283
284pub(crate) fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> FnSig {
285 match def {
286 CallableDef::Function(f) => fn_sig_for_fn(db, f),
287 CallableDef::Struct(s) => fn_sig_for_struct_constructor(db, s),
288 }
289}
290
291pub(crate) fn fn_sig_for_fn(db: &dyn HirDatabase, def: Function) -> FnSig {
292 let data = def.data(db.upcast());
293 let resolver = def.id.resolver(db.upcast());
294 let params = data
295 .params()
296 .iter()
297 .map(|tr| Ty::from_hir(db, &resolver, data.type_ref_map(), *tr).0)
298 .collect::<Vec<_>>();
299 let ret = Ty::from_hir(db, &resolver, data.type_ref_map(), *data.ret_type()).0;
300 FnSig::from_params_and_return(params, ret)
301}
302
303pub(crate) fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: Struct) -> FnSig {
304 let data = def.data(db.upcast());
305 let resolver = def.id.resolver(db.upcast());
306 let params = data
307 .fields
308 .iter()
309 .map(|(_, field)| Ty::from_hir(db, &resolver, data.type_ref_map(), field.type_ref).0)
310 .collect::<Vec<_>>();
311 let ret = type_for_struct(db, def);
312 FnSig::from_params_and_return(params, ret)
313}
314
315fn type_for_struct_constructor(db: &dyn HirDatabase, def: Struct) -> Ty {
317 let struct_data = db.struct_data(def.id);
318 if struct_data.kind == StructKind::Tuple {
319 TyKind::FnDef(def.into(), Substitution::empty()).intern()
320 } else {
321 type_for_struct(db, def)
322 }
323}
324
325fn type_for_struct(_db: &dyn HirDatabase, def: Struct) -> Ty {
326 TyKind::Struct(def).intern()
327}
328
329fn type_for_type_alias(db: &dyn HirDatabase, def: TypeAlias) -> Ty {
330 let data = def.data(db.upcast());
331 let resolver = def.id.resolver(db.upcast());
332 let type_ref = def.type_ref(db);
333 Ty::from_hir(db, &resolver, data.type_ref_map(), type_ref).0
334}
335
336pub mod diagnostics {
337 use crate::diagnostics::{CyclicType, PrivateAccess, UnresolvedType};
338 use crate::{
339 diagnostics::DiagnosticSink,
340 type_ref::{LocalTypeRefId, TypeRefSourceMap},
341 FileId, HirDatabase,
342 };
343
344 #[derive(Debug, PartialEq, Eq, Clone)]
345 pub(crate) enum LowerDiagnostic {
346 UnresolvedType { id: LocalTypeRefId },
347 TypeIsPrivate { id: LocalTypeRefId },
348 CyclicType { id: LocalTypeRefId },
349 }
350
351 impl LowerDiagnostic {
352 pub(crate) fn add_to(
353 &self,
354 _db: &dyn HirDatabase,
355 file_id: FileId,
356 source_map: &TypeRefSourceMap,
357 sink: &mut DiagnosticSink,
358 ) {
359 match self {
360 LowerDiagnostic::UnresolvedType { id } => sink.push(UnresolvedType {
361 file: file_id,
362 type_ref: source_map.type_ref_syntax(*id).unwrap(),
363 }),
364 LowerDiagnostic::CyclicType { id } => sink.push(CyclicType {
365 file: file_id,
366 type_ref: source_map.type_ref_syntax(*id).unwrap(),
367 }),
368 LowerDiagnostic::TypeIsPrivate { id } => sink.push(PrivateAccess {
369 file: file_id,
370 expr: source_map.type_ref_syntax(*id).unwrap().syntax_node_ptr(),
371 }),
372 }
373 }
374 }
375}