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,
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.clone()) }
670                    .into()
671            }
672            InferenceDiagnostic::UnresolvedField {
673                expr,
674                receiver,
675                name,
676                method_with_same_name_exists,
677            } => {
678                let expr = expr_syntax(*expr)?;
679                UnresolvedField {
680                    expr,
681                    name: name.clone(),
682                    receiver: Type::new(db, def, receiver.clone()),
683                    method_with_same_name_exists: *method_with_same_name_exists,
684                }
685                .into()
686            }
687            InferenceDiagnostic::UnresolvedMethodCall {
688                expr,
689                receiver,
690                name,
691                field_with_same_name,
692                assoc_func_with_same_name,
693            } => {
694                let expr = expr_syntax(*expr)?;
695                UnresolvedMethodCall {
696                    expr,
697                    name: name.clone(),
698                    receiver: Type::new(db, def, receiver.clone()),
699                    field_with_same_name: field_with_same_name
700                        .clone()
701                        .map(|ty| Type::new(db, def, ty)),
702                    assoc_func_with_same_name: assoc_func_with_same_name.map(Into::into),
703                }
704                .into()
705            }
706            &InferenceDiagnostic::UnresolvedAssocItem { id } => {
707                let expr_or_pat = expr_or_pat_syntax(id)?;
708                UnresolvedAssocItem { expr_or_pat }.into()
709            }
710            &InferenceDiagnostic::UnresolvedIdent { id } => {
711                let node = match id {
712                    ExprOrPatId::ExprId(id) => match source_map.expr_syntax(id) {
713                        Ok(syntax) => syntax.map(|it| (it, None)),
714                        Err(SyntheticSyntax) => source_map
715                            .format_args_implicit_capture(id)?
716                            .map(|(node, range)| (node.wrap_left(), Some(range))),
717                    },
718                    ExprOrPatId::PatId(id) => pat_syntax(id)?.map(|it| (it, None)),
719                };
720                UnresolvedIdent { node }.into()
721            }
722            &InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
723                let expr = expr_syntax(expr)?;
724                BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()
725            }
726            InferenceDiagnostic::TypedHole { expr, expected } => {
727                let expr = expr_syntax(*expr)?;
728                TypedHole { expr, expected: Type::new(db, def, expected.clone()) }.into()
729            }
730            &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => {
731                let expr_or_pat = match pat {
732                    ExprOrPatId::ExprId(expr) => expr_syntax(expr)?,
733                    ExprOrPatId::PatId(pat) => {
734                        let InFile { file_id, value } = pat_syntax(pat)?;
735
736                        // cast from Either<Pat, SelfParam> -> Either<_, Pat>
737                        let ptr = AstPtr::try_from_raw(value.syntax_node_ptr())?;
738                        InFile { file_id, value: ptr }
739                    }
740                };
741                MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into()
742            }
743            InferenceDiagnostic::CastToUnsized { expr, cast_ty } => {
744                let expr = expr_syntax(*expr)?;
745                CastToUnsized { expr, cast_ty: Type::new(db, def, cast_ty.clone()) }.into()
746            }
747            InferenceDiagnostic::InvalidCast { expr, error, expr_ty, cast_ty } => {
748                let expr = expr_syntax(*expr)?;
749                let expr_ty = Type::new(db, def, expr_ty.clone());
750                let cast_ty = Type::new(db, def, cast_ty.clone());
751                InvalidCast { expr, error: *error, expr_ty, cast_ty }.into()
752            }
753            InferenceDiagnostic::TyDiagnostic { source, diag } => {
754                let source_map = match source {
755                    InferenceTyDiagnosticSource::Body => source_map,
756                    InferenceTyDiagnosticSource::Signature => sig_map,
757                };
758                Self::ty_diagnostic(diag, source_map, db)?
759            }
760            InferenceDiagnostic::PathDiagnostic { node, diag } => {
761                let source = expr_or_pat_syntax(*node)?;
762                let syntax = source.value.to_node(&db.parse_or_expand(source.file_id));
763                let path = match_ast! {
764                    match (syntax.syntax()) {
765                        ast::RecordExpr(it) => it.path()?,
766                        ast::RecordPat(it) => it.path()?,
767                        ast::TupleStructPat(it) => it.path()?,
768                        ast::PathExpr(it) => it.path()?,
769                        ast::PathPat(it) => it.path()?,
770                        _ => return None,
771                    }
772                };
773                Self::path_diagnostic(diag, source.with_value(path))?
774            }
775            &InferenceDiagnostic::MethodCallIncorrectGenericsLen {
776                expr,
777                provided_count,
778                expected_count,
779                kind,
780                def,
781            } => {
782                let syntax = expr_syntax(expr)?;
783                let file_id = syntax.file_id;
784                let syntax =
785                    syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
786                let generics_or_name = syntax
787                    .generic_arg_list()
788                    .map(Either::Left)
789                    .or_else(|| syntax.name_ref().map(Either::Right))?;
790                let generics_or_name = InFile::new(file_id, AstPtr::new(&generics_or_name));
791                IncorrectGenericsLen {
792                    generics_or_segment: generics_or_name,
793                    kind,
794                    provided: provided_count,
795                    expected: expected_count,
796                    def: def.into(),
797                }
798                .into()
799            }
800            &InferenceDiagnostic::MethodCallIncorrectGenericsOrder {
801                expr,
802                param_id,
803                arg_idx,
804                has_self_arg,
805            } => {
806                let syntax = expr_syntax(expr)?;
807                let file_id = syntax.file_id;
808                let syntax =
809                    syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
810                let generic_args = syntax.generic_arg_list()?;
811                let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
812                let provided_arg = InFile::new(file_id, AstPtr::new(&provided_arg));
813                let expected_kind = GenericArgKind::from_id(param_id);
814                IncorrectGenericsOrder { provided_arg, expected_kind }.into()
815            }
816        })
817    }
818
819    fn path_diagnostic(
820        diag: &PathLoweringDiagnostic,
821        path: InFile<ast::Path>,
822    ) -> Option<AnyDiagnostic<'db>> {
823        Some(match *diag {
824            PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => {
825                let segment = hir_segment_to_ast_segment(&path.value, segment)?;
826
827                if let Some(rtn) = segment.return_type_syntax() {
828                    // RTN errors are emitted as `GenericArgsProhibited` or `ParenthesizedGenericArgsWithoutFnTrait`.
829                    return Some(BadRtn { rtn: path.with_value(AstPtr::new(&rtn)) }.into());
830                }
831
832                let args = if let Some(generics) = segment.generic_arg_list() {
833                    AstPtr::new(&generics).wrap_left()
834                } else {
835                    AstPtr::new(&segment.parenthesized_arg_list()?).wrap_right()
836                };
837                let args = path.with_value(args);
838                GenericArgsProhibited { args, reason }.into()
839            }
840            PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment } => {
841                let segment = hir_segment_to_ast_segment(&path.value, segment)?;
842
843                if let Some(rtn) = segment.return_type_syntax() {
844                    // RTN errors are emitted as `GenericArgsProhibited` or `ParenthesizedGenericArgsWithoutFnTrait`.
845                    return Some(BadRtn { rtn: path.with_value(AstPtr::new(&rtn)) }.into());
846                }
847
848                let args = AstPtr::new(&segment.parenthesized_arg_list()?);
849                let args = path.with_value(args);
850                ParenthesizedGenericArgsWithoutFnTrait { args }.into()
851            }
852            PathLoweringDiagnostic::IncorrectGenericsLen {
853                generics_source,
854                provided_count,
855                expected_count,
856                kind,
857                def,
858            } => {
859                let generics_or_segment =
860                    path_generics_source_to_ast(&path.value, generics_source)?;
861                let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
862                IncorrectGenericsLen {
863                    generics_or_segment,
864                    kind,
865                    provided: provided_count,
866                    expected: expected_count,
867                    def: def.into(),
868                }
869                .into()
870            }
871            PathLoweringDiagnostic::IncorrectGenericsOrder {
872                generics_source,
873                param_id,
874                arg_idx,
875                has_self_arg,
876            } => {
877                let generic_args =
878                    path_generics_source_to_ast(&path.value, generics_source)?.left()?;
879                let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
880                let provided_arg = path.with_value(AstPtr::new(&provided_arg));
881                let expected_kind = GenericArgKind::from_id(param_id);
882                IncorrectGenericsOrder { provided_arg, expected_kind }.into()
883            }
884            PathLoweringDiagnostic::MissingLifetime { generics_source, expected_count, def }
885            | PathLoweringDiagnostic::ElisionFailure { generics_source, expected_count, def } => {
886                let generics_or_segment =
887                    path_generics_source_to_ast(&path.value, generics_source)?;
888                let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
889                MissingLifetime { generics_or_segment, expected: expected_count, def: def.into() }
890                    .into()
891            }
892            PathLoweringDiagnostic::ElidedLifetimesInPath {
893                generics_source,
894                expected_count,
895                def,
896                hard_error,
897            } => {
898                let generics_or_segment =
899                    path_generics_source_to_ast(&path.value, generics_source)?;
900                let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
901                ElidedLifetimesInPath {
902                    generics_or_segment,
903                    expected: expected_count,
904                    def: def.into(),
905                    hard_error,
906                }
907                .into()
908            }
909        })
910    }
911
912    pub(crate) fn ty_diagnostic(
913        diag: &TyLoweringDiagnostic,
914        source_map: &ExpressionStoreSourceMap,
915        db: &'db dyn HirDatabase,
916    ) -> Option<AnyDiagnostic<'db>> {
917        let Ok(source) = source_map.type_syntax(diag.source) else {
918            stdx::never!("error on synthetic type syntax");
919            return None;
920        };
921        let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id));
922        Some(match &diag.kind {
923            TyLoweringDiagnosticKind::PathDiagnostic(diag) => {
924                let ast::Type::PathType(syntax) = syntax() else { return None };
925                Self::path_diagnostic(diag, source.with_value(syntax.path()?))?
926            }
927        })
928    }
929}
930
931fn path_generics_source_to_ast(
932    path: &ast::Path,
933    generics_source: PathGenericsSource,
934) -> Option<Either<ast::GenericArgList, ast::NameRef>> {
935    Some(match generics_source {
936        PathGenericsSource::Segment(segment) => {
937            let segment = hir_segment_to_ast_segment(path, segment)?;
938            segment
939                .generic_arg_list()
940                .map(Either::Left)
941                .or_else(|| segment.name_ref().map(Either::Right))?
942        }
943        PathGenericsSource::AssocType { segment, assoc_type } => {
944            let segment = hir_segment_to_ast_segment(path, segment)?;
945            let segment_args = segment.generic_arg_list()?;
946            let assoc = hir_assoc_type_binding_to_ast(&segment_args, assoc_type)?;
947            assoc
948                .generic_arg_list()
949                .map(Either::Left)
950                .or_else(|| assoc.name_ref().map(Either::Right))?
951        }
952    })
953}