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