hir_ty/infer/
diagnostics.rs

1//! This file contains the [`Diagnostics`] type used during inference,
2//! and a wrapper around [`TyLoweringContext`] ([`InferenceTyLoweringContext`]) that replaces
3//! it and takes care of diagnostics in inference.
4
5use std::cell::RefCell;
6use std::ops::{Deref, DerefMut};
7
8use either::Either;
9use hir_def::GenericDefId;
10use hir_def::expr_store::ExpressionStore;
11use hir_def::expr_store::path::Path;
12use hir_def::{hir::ExprOrPatId, resolver::Resolver};
13use la_arena::{Idx, RawIdx};
14use thin_vec::ThinVec;
15
16use crate::{
17    InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnostic,
18    db::HirDatabase,
19    lower::path::{PathDiagnosticCallback, PathLoweringContext},
20    lower::{LifetimeElisionKind, TyLoweringContext},
21};
22
23// Unfortunately, this struct needs to use interior mutability (but we encapsulate it)
24// because when lowering types and paths we hold a `TyLoweringContext` that holds a reference
25// to our resolver and so we cannot have mutable reference, but we really want to have
26// ability to dispatch diagnostics during this work otherwise the code becomes a complete mess.
27#[derive(Debug, Default, Clone)]
28pub(super) struct Diagnostics<'db>(RefCell<ThinVec<InferenceDiagnostic<'db>>>);
29
30impl<'db> Diagnostics<'db> {
31    pub(super) fn push(&self, diagnostic: InferenceDiagnostic<'db>) {
32        self.0.borrow_mut().push(diagnostic);
33    }
34
35    fn push_ty_diagnostics(
36        &self,
37        source: InferenceTyDiagnosticSource,
38        diagnostics: Vec<TyLoweringDiagnostic>,
39    ) {
40        self.0.borrow_mut().extend(
41            diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }),
42        );
43    }
44
45    pub(super) fn finish(self) -> ThinVec<InferenceDiagnostic<'db>> {
46        self.0.into_inner()
47    }
48}
49
50pub(crate) struct PathDiagnosticCallbackData<'a, 'db> {
51    node: ExprOrPatId,
52    diagnostics: &'a Diagnostics<'db>,
53}
54
55pub(super) struct InferenceTyLoweringContext<'db, 'a> {
56    ctx: TyLoweringContext<'db, 'a>,
57    diagnostics: &'a Diagnostics<'db>,
58    source: InferenceTyDiagnosticSource,
59}
60
61impl<'db, 'a> InferenceTyLoweringContext<'db, 'a> {
62    #[inline]
63    pub(super) fn new(
64        db: &'db dyn HirDatabase,
65        resolver: &'a Resolver<'db>,
66        store: &'a ExpressionStore,
67        diagnostics: &'a Diagnostics<'db>,
68        source: InferenceTyDiagnosticSource,
69        generic_def: GenericDefId,
70        lifetime_elision: LifetimeElisionKind<'db>,
71    ) -> Self {
72        Self {
73            ctx: TyLoweringContext::new(db, resolver, store, generic_def, lifetime_elision),
74            diagnostics,
75            source,
76        }
77    }
78
79    #[inline]
80    pub(super) fn at_path<'b>(
81        &'b mut self,
82        path: &'b Path,
83        node: ExprOrPatId,
84    ) -> PathLoweringContext<'b, 'a, 'db> {
85        let on_diagnostic = PathDiagnosticCallback {
86            data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, node }),
87            callback: |data, _, diag| {
88                let data = data.as_ref().right().unwrap();
89                data.diagnostics
90                    .push(InferenceDiagnostic::PathDiagnostic { node: data.node, diag });
91            },
92        };
93        PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
94    }
95
96    #[inline]
97    pub(super) fn at_path_forget_diagnostics<'b>(
98        &'b mut self,
99        path: &'b Path,
100    ) -> PathLoweringContext<'b, 'a, 'db> {
101        let on_diagnostic = PathDiagnosticCallback {
102            data: Either::Right(PathDiagnosticCallbackData {
103                diagnostics: self.diagnostics,
104                node: ExprOrPatId::ExprId(Idx::from_raw(RawIdx::from_u32(0))),
105            }),
106            callback: |_data, _, _diag| {},
107        };
108        PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
109    }
110
111    #[inline]
112    pub(super) fn forget_diagnostics(&mut self) {
113        self.ctx.diagnostics.clear();
114    }
115}
116
117impl<'db, 'a> Deref for InferenceTyLoweringContext<'db, 'a> {
118    type Target = TyLoweringContext<'db, 'a>;
119
120    #[inline]
121    fn deref(&self) -> &Self::Target {
122        &self.ctx
123    }
124}
125
126impl DerefMut for InferenceTyLoweringContext<'_, '_> {
127    #[inline]
128    fn deref_mut(&mut self) -> &mut Self::Target {
129        &mut self.ctx
130    }
131}
132
133impl Drop for InferenceTyLoweringContext<'_, '_> {
134    #[inline]
135    fn drop(&mut self) {
136        self.diagnostics
137            .push_ty_diagnostics(self.source, std::mem::take(&mut self.ctx.diagnostics));
138    }
139}