hir/
diagnostics.rs

1//! Re-export diagnostics such that clients of `hir` don't have to depend on
2//! low-level crates.
3//!
4//! This probably isn't the best way to do this -- ideally, diagnostics should
5//! be expressed in terms of hir types themselves.
6use cfg::{CfgExpr, CfgOptions};
7use either::Either;
8use hir_def::{
9    DefWithBodyId, GenericParamId, SyntheticSyntax,
10    expr_store::{
11        ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast,
12        hir_generic_arg_to_ast, hir_segment_to_ast_segment,
13    },
14    hir::ExprOrPatId,
15};
16use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name};
17use hir_ty::{
18    CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathGenericsSource,
19    PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
20    db::HirDatabase,
21    diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
22};
23use syntax::{
24    AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange,
25    ast::{self, HasGenericArgs},
26    match_ast,
27};
28use triomphe::Arc;
29
30use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type};
31
32pub use hir_def::VariantId;
33pub use hir_ty::{
34    GenericArgsProhibitedReason, IncorrectGenericsLenKind,
35    diagnostics::{CaseType, IncorrectCase},
36};
37
38macro_rules! diagnostics {
39    ($AnyDiagnostic:ident <$db:lifetime> -> $($diag:ident $(<$lt:lifetime>)?,)*) => {
40        #[derive(Debug)]
41        pub enum $AnyDiagnostic<$db> {$(
42            $diag(Box<$diag $(<$lt>)?>),
43        )*}
44
45        $(
46            impl<$db> From<$diag $(<$lt>)?> for $AnyDiagnostic<$db> {
47                fn from(d: $diag $(<$lt>)?) -> $AnyDiagnostic<$db> {
48                    $AnyDiagnostic::$diag(Box::new(d))
49                }
50            }
51        )*
52    };
53}
54
55diagnostics![AnyDiagnostic<'db> ->
56    AwaitOutsideOfAsync,
57    BreakOutsideOfLoop,
58    CastToUnsized<'db>,
59    ExpectedFunction<'db>,
60    InactiveCode,
61    IncoherentImpl,
62    IncorrectCase,
63    InvalidCast<'db>,
64    InvalidDeriveTarget,
65    MacroDefError,
66    MacroError,
67    MacroExpansionParseError,
68    MalformedDerive,
69    MismatchedArgCount,
70    MismatchedTupleStructPatArgCount,
71    MissingFields,
72    MissingMatchArms,
73    MissingUnsafe,
74    MovedOutOfRef<'db>,
75    NeedMut,
76    NonExhaustiveLet,
77    NoSuchField,
78    PrivateAssocItem,
79    PrivateField,
80    RemoveTrailingReturn,
81    RemoveUnnecessaryElse,
82    ReplaceFilterMapNextWithFindMap,
83    TraitImplIncorrectSafety,
84    TraitImplMissingAssocItems,
85    TraitImplOrphan,
86    TraitImplRedundantAssocItems,
87    TypedHole<'db>,
88    TypeMismatch<'db>,
89    UndeclaredLabel,
90    UnimplementedBuiltinMacro,
91    UnreachableLabel,
92    UnresolvedAssocItem,
93    UnresolvedExternCrate,
94    UnresolvedField<'db>,
95    UnresolvedImport,
96    UnresolvedMacroCall,
97    UnresolvedMethodCall<'db>,
98    UnresolvedModule,
99    UnresolvedIdent,
100    UnusedMut,
101    UnusedVariable,
102    GenericArgsProhibited,
103    ParenthesizedGenericArgsWithoutFnTrait,
104    BadRtn,
105    IncorrectGenericsLen,
106    IncorrectGenericsOrder,
107    MissingLifetime,
108    ElidedLifetimesInPath,
109];
110
111#[derive(Debug)]
112pub struct BreakOutsideOfLoop {
113    pub expr: InFile<ExprOrPatPtr>,
114    pub is_break: bool,
115    pub bad_value_break: bool,
116}
117
118#[derive(Debug)]
119pub struct TypedHole<'db> {
120    pub expr: InFile<ExprOrPatPtr>,
121    pub expected: Type<'db>,
122}
123
124#[derive(Debug)]
125pub struct UnresolvedModule {
126    pub decl: InFile<AstPtr<ast::Module>>,
127    pub candidates: Box<[String]>,
128}
129
130#[derive(Debug)]
131pub struct UnresolvedExternCrate {
132    pub decl: InFile<AstPtr<ast::ExternCrate>>,
133}
134
135#[derive(Debug)]
136pub struct UnresolvedImport {
137    pub decl: InFile<AstPtr<ast::UseTree>>,
138}
139
140#[derive(Debug, Clone, Eq, PartialEq)]
141pub struct UnresolvedMacroCall {
142    pub range: InFile<TextRange>,
143    pub path: ModPath,
144    pub is_bang: bool,
145}
146#[derive(Debug, Clone, Eq, PartialEq)]
147pub struct UnreachableLabel {
148    pub node: InFile<AstPtr<ast::Lifetime>>,
149    pub name: Name,
150}
151
152#[derive(Debug)]
153pub struct AwaitOutsideOfAsync {
154    pub node: InFile<AstPtr<ast::AwaitExpr>>,
155    pub location: String,
156}
157
158#[derive(Debug, Clone, Eq, PartialEq)]
159pub struct UndeclaredLabel {
160    pub node: InFile<AstPtr<ast::Lifetime>>,
161    pub name: Name,
162}
163
164#[derive(Debug, Clone, Eq, PartialEq)]
165pub struct InactiveCode {
166    pub node: InFile<SyntaxNodePtr>,
167    pub cfg: CfgExpr,
168    pub opts: CfgOptions,
169}
170
171#[derive(Debug, Clone, Eq, PartialEq)]
172pub struct MacroError {
173    pub range: InFile<TextRange>,
174    pub message: String,
175    pub error: bool,
176    pub kind: &'static str,
177}
178
179#[derive(Debug, Clone, Eq, PartialEq)]
180pub struct MacroExpansionParseError {
181    pub range: InFile<TextRange>,
182    pub errors: Arc<[SyntaxError]>,
183}
184
185#[derive(Debug, Clone, Eq, PartialEq)]
186pub struct MacroDefError {
187    pub node: InFile<AstPtr<ast::Macro>>,
188    pub message: String,
189    pub name: Option<TextRange>,
190}
191
192#[derive(Debug)]
193pub struct UnimplementedBuiltinMacro {
194    pub node: InFile<SyntaxNodePtr>,
195}
196
197#[derive(Debug)]
198pub struct InvalidDeriveTarget {
199    pub range: InFile<TextRange>,
200}
201
202#[derive(Debug)]
203pub struct MalformedDerive {
204    pub range: InFile<TextRange>,
205}
206
207#[derive(Debug)]
208pub struct NoSuchField {
209    pub field: InFile<AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>>,
210    pub private: Option<Field>,
211    pub variant: VariantId,
212}
213
214#[derive(Debug)]
215pub struct PrivateAssocItem {
216    pub expr_or_pat: InFile<ExprOrPatPtr>,
217    pub item: AssocItem,
218}
219
220#[derive(Debug)]
221pub struct MismatchedTupleStructPatArgCount {
222    pub expr_or_pat: InFile<ExprOrPatPtr>,
223    pub expected: usize,
224    pub found: usize,
225}
226
227#[derive(Debug)]
228pub struct ExpectedFunction<'db> {
229    pub call: InFile<ExprOrPatPtr>,
230    pub found: Type<'db>,
231}
232
233#[derive(Debug)]
234pub struct UnresolvedField<'db> {
235    pub expr: InFile<ExprOrPatPtr>,
236    pub receiver: Type<'db>,
237    pub name: Name,
238    pub method_with_same_name_exists: bool,
239}
240
241#[derive(Debug)]
242pub struct UnresolvedMethodCall<'db> {
243    pub expr: InFile<ExprOrPatPtr>,
244    pub receiver: Type<'db>,
245    pub name: Name,
246    pub field_with_same_name: Option<Type<'db>>,
247    pub assoc_func_with_same_name: Option<Function>,
248}
249
250#[derive(Debug)]
251pub struct UnresolvedAssocItem {
252    pub expr_or_pat: InFile<ExprOrPatPtr>,
253}
254
255#[derive(Debug)]
256pub struct UnresolvedIdent {
257    pub node: InFile<(ExprOrPatPtr, Option<TextRange>)>,
258}
259
260#[derive(Debug)]
261pub struct PrivateField {
262    pub expr: InFile<ExprOrPatPtr>,
263    pub field: Field,
264}
265
266#[derive(Debug, Clone, Copy, PartialEq, Eq)]
267pub enum UnsafeLint {
268    HardError,
269    UnsafeOpInUnsafeFn,
270    DeprecatedSafe2024,
271}
272
273#[derive(Debug)]
274pub struct MissingUnsafe {
275    pub node: InFile<ExprOrPatPtr>,
276    pub lint: UnsafeLint,
277    pub reason: UnsafetyReason,
278}
279
280#[derive(Debug)]
281pub struct MissingFields {
282    pub file: HirFileId,
283    pub field_list_parent: AstPtr<Either<ast::RecordExpr, ast::RecordPat>>,
284    pub field_list_parent_path: Option<AstPtr<ast::Path>>,
285    pub missed_fields: Vec<Name>,
286}
287
288#[derive(Debug)]
289pub struct ReplaceFilterMapNextWithFindMap {
290    pub file: HirFileId,
291    /// This expression is the whole method chain up to and including `.filter_map(..).next()`.
292    pub next_expr: AstPtr<ast::Expr>,
293}
294
295#[derive(Debug)]
296pub struct MismatchedArgCount {
297    pub call_expr: InFile<ExprOrPatPtr>,
298    pub expected: usize,
299    pub found: usize,
300}
301
302#[derive(Debug)]
303pub struct MissingMatchArms {
304    pub scrutinee_expr: InFile<AstPtr<ast::Expr>>,
305    pub uncovered_patterns: String,
306}
307
308#[derive(Debug)]
309pub struct NonExhaustiveLet {
310    pub pat: InFile<AstPtr<ast::Pat>>,
311    pub uncovered_patterns: String,
312}
313
314#[derive(Debug)]
315pub struct TypeMismatch<'db> {
316    pub expr_or_pat: InFile<ExprOrPatPtr>,
317    pub expected: Type<'db>,
318    pub actual: Type<'db>,
319}
320
321#[derive(Debug)]
322pub struct NeedMut {
323    pub local: Local,
324    pub span: InFile<SyntaxNodePtr>,
325}
326
327#[derive(Debug)]
328pub struct UnusedMut {
329    pub local: Local,
330}
331
332#[derive(Debug)]
333pub struct UnusedVariable {
334    pub local: Local,
335}
336
337#[derive(Debug)]
338pub struct MovedOutOfRef<'db> {
339    pub ty: Type<'db>,
340    pub span: InFile<SyntaxNodePtr>,
341}
342
343#[derive(Debug, PartialEq, Eq)]
344pub struct IncoherentImpl {
345    pub file_id: HirFileId,
346    pub impl_: AstPtr<ast::Impl>,
347}
348
349#[derive(Debug, PartialEq, Eq)]
350pub struct TraitImplOrphan {
351    pub file_id: HirFileId,
352    pub impl_: AstPtr<ast::Impl>,
353}
354
355// FIXME: Split this off into the corresponding 4 rustc errors
356#[derive(Debug, PartialEq, Eq)]
357pub struct TraitImplIncorrectSafety {
358    pub file_id: HirFileId,
359    pub impl_: AstPtr<ast::Impl>,
360    pub should_be_safe: bool,
361}
362
363#[derive(Debug, PartialEq, Eq)]
364pub struct TraitImplMissingAssocItems {
365    pub file_id: HirFileId,
366    pub impl_: AstPtr<ast::Impl>,
367    pub missing: Vec<(Name, AssocItem)>,
368}
369
370#[derive(Debug, PartialEq, Eq)]
371pub struct TraitImplRedundantAssocItems {
372    pub file_id: HirFileId,
373    pub trait_: Trait,
374    pub impl_: AstPtr<ast::Impl>,
375    pub assoc_item: (Name, AssocItem),
376}
377
378#[derive(Debug)]
379pub struct RemoveTrailingReturn {
380    pub return_expr: InFile<AstPtr<ast::ReturnExpr>>,
381}
382
383#[derive(Debug)]
384pub struct RemoveUnnecessaryElse {
385    pub if_expr: InFile<AstPtr<ast::IfExpr>>,
386}
387
388#[derive(Debug)]
389pub struct CastToUnsized<'db> {
390    pub expr: InFile<ExprOrPatPtr>,
391    pub cast_ty: Type<'db>,
392}
393
394#[derive(Debug)]
395pub struct InvalidCast<'db> {
396    pub expr: InFile<ExprOrPatPtr>,
397    pub error: CastError,
398    pub expr_ty: Type<'db>,
399    pub cast_ty: Type<'db>,
400}
401
402#[derive(Debug)]
403pub struct GenericArgsProhibited {
404    pub args: InFile<AstPtr<Either<ast::GenericArgList, ast::ParenthesizedArgList>>>,
405    pub reason: GenericArgsProhibitedReason,
406}
407
408#[derive(Debug)]
409pub struct ParenthesizedGenericArgsWithoutFnTrait {
410    pub args: InFile<AstPtr<ast::ParenthesizedArgList>>,
411}
412
413#[derive(Debug)]
414pub struct BadRtn {
415    pub rtn: InFile<AstPtr<ast::ReturnTypeSyntax>>,
416}
417
418#[derive(Debug)]
419pub struct IncorrectGenericsLen {
420    /// Points at the name if there are no generics.
421    pub generics_or_segment: InFile<AstPtr<Either<ast::GenericArgList, ast::NameRef>>>,
422    pub kind: IncorrectGenericsLenKind,
423    pub provided: u32,
424    pub expected: u32,
425    pub def: GenericDef,
426}
427
428#[derive(Debug)]
429pub struct MissingLifetime {
430    /// Points at the name if there are no generics.
431    pub generics_or_segment: InFile<AstPtr<Either<ast::GenericArgList, ast::NameRef>>>,
432    pub expected: u32,
433    pub def: GenericDef,
434}
435
436#[derive(Debug)]
437pub struct ElidedLifetimesInPath {
438    /// Points at the name if there are no generics.
439    pub generics_or_segment: InFile<AstPtr<Either<ast::GenericArgList, ast::NameRef>>>,
440    pub expected: u32,
441    pub def: GenericDef,
442    pub hard_error: bool,
443}
444
445#[derive(Debug, Clone, Copy, PartialEq, Eq)]
446pub enum GenericArgKind {
447    Lifetime,
448    Type,
449    Const,
450}
451
452impl GenericArgKind {
453    fn from_id(id: GenericParamId) -> Self {
454        match id {
455            GenericParamId::TypeParamId(_) => GenericArgKind::Type,
456            GenericParamId::ConstParamId(_) => GenericArgKind::Const,
457            GenericParamId::LifetimeParamId(_) => GenericArgKind::Lifetime,
458        }
459    }
460}
461
462#[derive(Debug)]
463pub struct IncorrectGenericsOrder {
464    pub provided_arg: InFile<AstPtr<ast::GenericArg>>,
465    pub expected_kind: GenericArgKind,
466}
467
468impl<'db> AnyDiagnostic<'db> {
469    pub(crate) fn body_validation_diagnostic(
470        db: &'db dyn HirDatabase,
471        diagnostic: BodyValidationDiagnostic,
472        source_map: &hir_def::expr_store::BodySourceMap,
473    ) -> Option<AnyDiagnostic<'db>> {
474        match diagnostic {
475            BodyValidationDiagnostic::RecordMissingFields { record, variant, missed_fields } => {
476                let variant_data = variant.fields(db);
477                let missed_fields = missed_fields
478                    .into_iter()
479                    .map(|idx| variant_data.fields()[idx].name.clone())
480                    .collect();
481
482                let record = match record {
483                    Either::Left(record_expr) => source_map.expr_syntax(record_expr).ok()?,
484                    Either::Right(record_pat) => source_map.pat_syntax(record_pat).ok()?,
485                };
486                let file = record.file_id;
487                let root = record.file_syntax(db);
488                match record.value.to_node(&root) {
489                    Either::Left(ast::Expr::RecordExpr(record_expr)) => {
490                        if record_expr.record_expr_field_list().is_some() {
491                            let field_list_parent_path =
492                                record_expr.path().map(|path| AstPtr::new(&path));
493                            return Some(
494                                MissingFields {
495                                    file,
496                                    field_list_parent: AstPtr::new(&Either::Left(record_expr)),
497                                    field_list_parent_path,
498                                    missed_fields,
499                                }
500                                .into(),
501                            );
502                        }
503                    }
504                    Either::Right(ast::Pat::RecordPat(record_pat)) => {
505                        if record_pat.record_pat_field_list().is_some() {
506                            let field_list_parent_path =
507                                record_pat.path().map(|path| AstPtr::new(&path));
508                            return Some(
509                                MissingFields {
510                                    file,
511                                    field_list_parent: AstPtr::new(&Either::Right(record_pat)),
512                                    field_list_parent_path,
513                                    missed_fields,
514                                }
515                                .into(),
516                            );
517                        }
518                    }
519                    _ => {}
520                }
521            }
522            BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { method_call_expr } => {
523                if let Ok(next_source_ptr) = source_map.expr_syntax(method_call_expr) {
524                    return Some(
525                        ReplaceFilterMapNextWithFindMap {
526                            file: next_source_ptr.file_id,
527                            next_expr: next_source_ptr.value.cast()?,
528                        }
529                        .into(),
530                    );
531                }
532            }
533            BodyValidationDiagnostic::MissingMatchArms { match_expr, uncovered_patterns } => {
534                match source_map.expr_syntax(match_expr) {
535                    Ok(source_ptr) => {
536                        let root = source_ptr.file_syntax(db);
537                        if let Either::Left(ast::Expr::MatchExpr(match_expr)) =
538                            &source_ptr.value.to_node(&root)
539                        {
540                            match match_expr.expr() {
541                                Some(scrut_expr) if match_expr.match_arm_list().is_some() => {
542                                    return Some(
543                                        MissingMatchArms {
544                                            scrutinee_expr: InFile::new(
545                                                source_ptr.file_id,
546                                                AstPtr::new(&scrut_expr),
547                                            ),
548                                            uncovered_patterns,
549                                        }
550                                        .into(),
551                                    );
552                                }
553                                _ => {}
554                            }
555                        }
556                    }
557                    Err(SyntheticSyntax) => (),
558                }
559            }
560            BodyValidationDiagnostic::NonExhaustiveLet { pat, uncovered_patterns } => {
561                match source_map.pat_syntax(pat) {
562                    Ok(source_ptr) => {
563                        if let Some(ast_pat) = source_ptr.value.cast::<ast::Pat>() {
564                            return Some(
565                                NonExhaustiveLet {
566                                    pat: InFile::new(source_ptr.file_id, ast_pat),
567                                    uncovered_patterns,
568                                }
569                                .into(),
570                            );
571                        }
572                    }
573                    Err(SyntheticSyntax) => {}
574                }
575            }
576            BodyValidationDiagnostic::RemoveTrailingReturn { return_expr } => {
577                if let Ok(source_ptr) = source_map.expr_syntax(return_expr) {
578                    // Filters out desugared return expressions (e.g. desugared try operators).
579                    if let Some(ptr) = source_ptr.value.cast::<ast::ReturnExpr>() {
580                        return Some(
581                            RemoveTrailingReturn {
582                                return_expr: InFile::new(source_ptr.file_id, ptr),
583                            }
584                            .into(),
585                        );
586                    }
587                }
588            }
589            BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr } => {
590                if let Ok(source_ptr) = source_map.expr_syntax(if_expr)
591                    && let Some(ptr) = source_ptr.value.cast::<ast::IfExpr>()
592                {
593                    return Some(
594                        RemoveUnnecessaryElse { if_expr: InFile::new(source_ptr.file_id, ptr) }
595                            .into(),
596                    );
597                }
598            }
599        }
600        None
601    }
602
603    pub(crate) fn inference_diagnostic(
604        db: &'db dyn HirDatabase,
605        def: DefWithBodyId,
606        d: &InferenceDiagnostic,
607        source_map: &hir_def::expr_store::BodySourceMap,
608        sig_map: &hir_def::expr_store::ExpressionStoreSourceMap,
609    ) -> Option<AnyDiagnostic<'db>> {
610        let expr_syntax = |expr| {
611            source_map
612                .expr_syntax(expr)
613                .inspect_err(|_| stdx::never!("inference diagnostic in desugared expr"))
614                .ok()
615        };
616        let pat_syntax = |pat| {
617            source_map
618                .pat_syntax(pat)
619                .inspect_err(|_| stdx::never!("inference diagnostic in desugared pattern"))
620                .ok()
621        };
622        let expr_or_pat_syntax = |id| match id {
623            ExprOrPatId::ExprId(expr) => expr_syntax(expr),
624            ExprOrPatId::PatId(pat) => pat_syntax(pat),
625        };
626        Some(match d {
627            &InferenceDiagnostic::NoSuchField { field: expr, private, variant } => {
628                let expr_or_pat = match expr {
629                    ExprOrPatId::ExprId(expr) => {
630                        source_map.field_syntax(expr).map(AstPtr::wrap_left)
631                    }
632                    ExprOrPatId::PatId(pat) => source_map.pat_field_syntax(pat),
633                };
634                let private = private.map(|id| Field { id, parent: variant.into() });
635                NoSuchField { field: expr_or_pat, private, variant }.into()
636            }
637            &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
638                MismatchedArgCount { call_expr: expr_syntax(call_expr)?, expected, found }.into()
639            }
640            &InferenceDiagnostic::PrivateField { expr, field } => {
641                let expr = expr_syntax(expr)?;
642                let field = field.into();
643                PrivateField { expr, field }.into()
644            }
645            &InferenceDiagnostic::PrivateAssocItem { id, item } => {
646                let expr_or_pat = expr_or_pat_syntax(id)?;
647                let item = item.into();
648                PrivateAssocItem { expr_or_pat, item }.into()
649            }
650            InferenceDiagnostic::ExpectedFunction { call_expr, found } => {
651                let call_expr = expr_syntax(*call_expr)?;
652                ExpectedFunction { call: call_expr, found: Type::new(db, def, found.as_ref()) }
653                    .into()
654            }
655            InferenceDiagnostic::UnresolvedField {
656                expr,
657                receiver,
658                name,
659                method_with_same_name_exists,
660            } => {
661                let expr = expr_syntax(*expr)?;
662                UnresolvedField {
663                    expr,
664                    name: name.clone(),
665                    receiver: Type::new(db, def, receiver.as_ref()),
666                    method_with_same_name_exists: *method_with_same_name_exists,
667                }
668                .into()
669            }
670            InferenceDiagnostic::UnresolvedMethodCall {
671                expr,
672                receiver,
673                name,
674                field_with_same_name,
675                assoc_func_with_same_name,
676            } => {
677                let expr = expr_syntax(*expr)?;
678                UnresolvedMethodCall {
679                    expr,
680                    name: name.clone(),
681                    receiver: Type::new(db, def, receiver.as_ref()),
682                    field_with_same_name: field_with_same_name
683                        .as_ref()
684                        .map(|ty| Type::new(db, def, ty.as_ref())),
685                    assoc_func_with_same_name: assoc_func_with_same_name.map(Into::into),
686                }
687                .into()
688            }
689            &InferenceDiagnostic::UnresolvedAssocItem { id } => {
690                let expr_or_pat = expr_or_pat_syntax(id)?;
691                UnresolvedAssocItem { expr_or_pat }.into()
692            }
693            &InferenceDiagnostic::UnresolvedIdent { id } => {
694                let node = match id {
695                    ExprOrPatId::ExprId(id) => match source_map.expr_syntax(id) {
696                        Ok(syntax) => syntax.map(|it| (it, None)),
697                        Err(SyntheticSyntax) => source_map
698                            .format_args_implicit_capture(id)?
699                            .map(|(node, range)| (node.wrap_left(), Some(range))),
700                    },
701                    ExprOrPatId::PatId(id) => pat_syntax(id)?.map(|it| (it, None)),
702                };
703                UnresolvedIdent { node }.into()
704            }
705            &InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
706                let expr = expr_syntax(expr)?;
707                BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()
708            }
709            InferenceDiagnostic::TypedHole { expr, expected } => {
710                let expr = expr_syntax(*expr)?;
711                TypedHole { expr, expected: Type::new(db, def, expected.as_ref()) }.into()
712            }
713            &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => {
714                let expr_or_pat = match pat {
715                    ExprOrPatId::ExprId(expr) => expr_syntax(expr)?,
716                    ExprOrPatId::PatId(pat) => {
717                        let InFile { file_id, value } = pat_syntax(pat)?;
718
719                        // cast from Either<Pat, SelfParam> -> Either<_, Pat>
720                        let ptr = AstPtr::try_from_raw(value.syntax_node_ptr())?;
721                        InFile { file_id, value: ptr }
722                    }
723                };
724                MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into()
725            }
726            InferenceDiagnostic::CastToUnsized { expr, cast_ty } => {
727                let expr = expr_syntax(*expr)?;
728                CastToUnsized { expr, cast_ty: Type::new(db, def, cast_ty.as_ref()) }.into()
729            }
730            InferenceDiagnostic::InvalidCast { expr, error, expr_ty, cast_ty } => {
731                let expr = expr_syntax(*expr)?;
732                let expr_ty = Type::new(db, def, expr_ty.as_ref());
733                let cast_ty = Type::new(db, def, cast_ty.as_ref());
734                InvalidCast { expr, error: *error, expr_ty, cast_ty }.into()
735            }
736            InferenceDiagnostic::TyDiagnostic { source, diag } => {
737                let source_map = match source {
738                    InferenceTyDiagnosticSource::Body => source_map,
739                    InferenceTyDiagnosticSource::Signature => sig_map,
740                };
741                Self::ty_diagnostic(diag, source_map, db)?
742            }
743            InferenceDiagnostic::PathDiagnostic { node, diag } => {
744                let source = expr_or_pat_syntax(*node)?;
745                let syntax = source.value.to_node(&db.parse_or_expand(source.file_id));
746                let path = match_ast! {
747                    match (syntax.syntax()) {
748                        ast::RecordExpr(it) => it.path()?,
749                        ast::RecordPat(it) => it.path()?,
750                        ast::TupleStructPat(it) => it.path()?,
751                        ast::PathExpr(it) => it.path()?,
752                        ast::PathPat(it) => it.path()?,
753                        _ => return None,
754                    }
755                };
756                Self::path_diagnostic(diag, source.with_value(path))?
757            }
758            &InferenceDiagnostic::MethodCallIncorrectGenericsLen {
759                expr,
760                provided_count,
761                expected_count,
762                kind,
763                def,
764            } => {
765                let syntax = expr_syntax(expr)?;
766                let file_id = syntax.file_id;
767                let syntax =
768                    syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
769                let generics_or_name = syntax
770                    .generic_arg_list()
771                    .map(Either::Left)
772                    .or_else(|| syntax.name_ref().map(Either::Right))?;
773                let generics_or_name = InFile::new(file_id, AstPtr::new(&generics_or_name));
774                IncorrectGenericsLen {
775                    generics_or_segment: generics_or_name,
776                    kind,
777                    provided: provided_count,
778                    expected: expected_count,
779                    def: def.into(),
780                }
781                .into()
782            }
783            &InferenceDiagnostic::MethodCallIncorrectGenericsOrder {
784                expr,
785                param_id,
786                arg_idx,
787                has_self_arg,
788            } => {
789                let syntax = expr_syntax(expr)?;
790                let file_id = syntax.file_id;
791                let syntax =
792                    syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
793                let generic_args = syntax.generic_arg_list()?;
794                let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
795                let provided_arg = InFile::new(file_id, AstPtr::new(&provided_arg));
796                let expected_kind = GenericArgKind::from_id(param_id);
797                IncorrectGenericsOrder { provided_arg, expected_kind }.into()
798            }
799        })
800    }
801
802    fn path_diagnostic(
803        diag: &PathLoweringDiagnostic,
804        path: InFile<ast::Path>,
805    ) -> Option<AnyDiagnostic<'db>> {
806        Some(match *diag {
807            PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => {
808                let segment = hir_segment_to_ast_segment(&path.value, segment)?;
809
810                if let Some(rtn) = segment.return_type_syntax() {
811                    // RTN errors are emitted as `GenericArgsProhibited` or `ParenthesizedGenericArgsWithoutFnTrait`.
812                    return Some(BadRtn { rtn: path.with_value(AstPtr::new(&rtn)) }.into());
813                }
814
815                let args = if let Some(generics) = segment.generic_arg_list() {
816                    AstPtr::new(&generics).wrap_left()
817                } else {
818                    AstPtr::new(&segment.parenthesized_arg_list()?).wrap_right()
819                };
820                let args = path.with_value(args);
821                GenericArgsProhibited { args, reason }.into()
822            }
823            PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment } => {
824                let segment = hir_segment_to_ast_segment(&path.value, segment)?;
825
826                if let Some(rtn) = segment.return_type_syntax() {
827                    // RTN errors are emitted as `GenericArgsProhibited` or `ParenthesizedGenericArgsWithoutFnTrait`.
828                    return Some(BadRtn { rtn: path.with_value(AstPtr::new(&rtn)) }.into());
829                }
830
831                let args = AstPtr::new(&segment.parenthesized_arg_list()?);
832                let args = path.with_value(args);
833                ParenthesizedGenericArgsWithoutFnTrait { args }.into()
834            }
835            PathLoweringDiagnostic::IncorrectGenericsLen {
836                generics_source,
837                provided_count,
838                expected_count,
839                kind,
840                def,
841            } => {
842                let generics_or_segment =
843                    path_generics_source_to_ast(&path.value, generics_source)?;
844                let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
845                IncorrectGenericsLen {
846                    generics_or_segment,
847                    kind,
848                    provided: provided_count,
849                    expected: expected_count,
850                    def: def.into(),
851                }
852                .into()
853            }
854            PathLoweringDiagnostic::IncorrectGenericsOrder {
855                generics_source,
856                param_id,
857                arg_idx,
858                has_self_arg,
859            } => {
860                let generic_args =
861                    path_generics_source_to_ast(&path.value, generics_source)?.left()?;
862                let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
863                let provided_arg = path.with_value(AstPtr::new(&provided_arg));
864                let expected_kind = GenericArgKind::from_id(param_id);
865                IncorrectGenericsOrder { provided_arg, expected_kind }.into()
866            }
867            PathLoweringDiagnostic::MissingLifetime { generics_source, expected_count, def }
868            | PathLoweringDiagnostic::ElisionFailure { generics_source, expected_count, def } => {
869                let generics_or_segment =
870                    path_generics_source_to_ast(&path.value, generics_source)?;
871                let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
872                MissingLifetime { generics_or_segment, expected: expected_count, def: def.into() }
873                    .into()
874            }
875            PathLoweringDiagnostic::ElidedLifetimesInPath {
876                generics_source,
877                expected_count,
878                def,
879                hard_error,
880            } => {
881                let generics_or_segment =
882                    path_generics_source_to_ast(&path.value, generics_source)?;
883                let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
884                ElidedLifetimesInPath {
885                    generics_or_segment,
886                    expected: expected_count,
887                    def: def.into(),
888                    hard_error,
889                }
890                .into()
891            }
892        })
893    }
894
895    pub(crate) fn ty_diagnostic(
896        diag: &TyLoweringDiagnostic,
897        source_map: &ExpressionStoreSourceMap,
898        db: &'db dyn HirDatabase,
899    ) -> Option<AnyDiagnostic<'db>> {
900        let Ok(source) = source_map.type_syntax(diag.source) else {
901            stdx::never!("error on synthetic type syntax");
902            return None;
903        };
904        let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id));
905        Some(match &diag.kind {
906            TyLoweringDiagnosticKind::PathDiagnostic(diag) => {
907                let ast::Type::PathType(syntax) = syntax() else { return None };
908                Self::path_diagnostic(diag, source.with_value(syntax.path()?))?
909            }
910        })
911    }
912}
913
914fn path_generics_source_to_ast(
915    path: &ast::Path,
916    generics_source: PathGenericsSource,
917) -> Option<Either<ast::GenericArgList, ast::NameRef>> {
918    Some(match generics_source {
919        PathGenericsSource::Segment(segment) => {
920            let segment = hir_segment_to_ast_segment(path, segment)?;
921            segment
922                .generic_arg_list()
923                .map(Either::Left)
924                .or_else(|| segment.name_ref().map(Either::Right))?
925        }
926        PathGenericsSource::AssocType { segment, assoc_type } => {
927            let segment = hir_segment_to_ast_segment(path, segment)?;
928            let segment_args = segment.generic_arg_list()?;
929            let assoc = hir_assoc_type_binding_to_ast(&segment_args, assoc_type)?;
930            assoc
931                .generic_arg_list()
932                .map(Either::Left)
933                .or_else(|| assoc.name_ref().map(Either::Right))?
934        }
935    })
936}