Skip to main content

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, HasModule, 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, ParamEnvAndCrate,
19    PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
20    db::HirDatabase,
21    diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
22    display::{DisplayTarget, HirDisplay},
23    next_solver::DbInterner,
24    solver_errors::SolverDiagnosticKind,
25};
26use stdx::{impl_from, never};
27use syntax::{
28    AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange,
29    ast::{self, HasGenericArgs},
30    match_ast,
31};
32use triomphe::Arc;
33
34use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, Variant};
35
36pub use hir_def::VariantId;
37pub use hir_ty::{
38    GenericArgsProhibitedReason, IncorrectGenericsLenKind,
39    diagnostics::{CaseType, IncorrectCase},
40};
41
42#[derive(Debug, Clone)]
43pub enum SpanAst {
44    Expr(ast::Expr),
45    Pat(ast::Pat),
46    Type(ast::Type),
47}
48const _: () = {
49    use syntax::ast::*;
50    impl_from!(Expr, Pat, Type for SpanAst);
51};
52
53impl From<Either<ast::Expr, ast::Pat>> for SpanAst {
54    fn from(value: Either<ast::Expr, ast::Pat>) -> Self {
55        match value {
56            Either::Left(it) => it.into(),
57            Either::Right(it) => it.into(),
58        }
59    }
60}
61
62impl ast::AstNode for SpanAst {
63    fn can_cast(kind: syntax::SyntaxKind) -> bool {
64        ast::Expr::can_cast(kind) || ast::Pat::can_cast(kind) || ast::Type::can_cast(kind)
65    }
66
67    fn cast(syntax: syntax::SyntaxNode) -> Option<Self> {
68        ast::Expr::cast(syntax.clone())
69            .map(SpanAst::Expr)
70            .or_else(|| ast::Pat::cast(syntax.clone()).map(SpanAst::Pat))
71            .or_else(|| ast::Type::cast(syntax).map(SpanAst::Type))
72    }
73
74    fn syntax(&self) -> &syntax::SyntaxNode {
75        match self {
76            SpanAst::Expr(it) => it.syntax(),
77            SpanAst::Pat(it) => it.syntax(),
78            SpanAst::Type(it) => it.syntax(),
79        }
80    }
81}
82
83pub type SpanSyntax = InFile<AstPtr<SpanAst>>;
84
85macro_rules! diagnostics {
86    ($AnyDiagnostic:ident <$db:lifetime> -> $($diag:ident $(<$lt:lifetime>)?,)*) => {
87        #[derive(Debug)]
88        pub enum $AnyDiagnostic<$db> {$(
89            $diag(Box<$diag $(<$lt>)?>),
90        )*}
91
92        $(
93            impl<$db> From<$diag $(<$lt>)?> for $AnyDiagnostic<$db> {
94                fn from(d: $diag $(<$lt>)?) -> $AnyDiagnostic<$db> {
95                    $AnyDiagnostic::$diag(Box::new(d))
96                }
97            }
98        )*
99    };
100}
101
102diagnostics![AnyDiagnostic<'db> ->
103    AwaitOutsideOfAsync,
104    BreakOutsideOfLoop,
105    CastToUnsized<'db>,
106    ExpectedFunction<'db>,
107    FunctionalRecordUpdateOnNonStruct,
108    GenericDefaultRefersToSelf,
109    InactiveCode,
110    IncoherentImpl,
111    IncorrectCase,
112    IncorrectGenericsLen,
113    IncorrectGenericsOrder,
114    InvalidCast<'db>,
115    InvalidDeriveTarget,
116    InvalidLhsOfAssignment,
117    MacroDefError,
118    MacroError,
119    MacroExpansionParseError,
120    MalformedDerive,
121    MismatchedArgCount,
122    MismatchedTupleStructPatArgCount,
123    MissingFields,
124    MissingMatchArms,
125    MissingUnsafe,
126    MovedOutOfRef<'db>,
127    NeedMut,
128    NonExhaustiveLet,
129    NonExhaustiveRecordExpr,
130    NoSuchField,
131    MismatchedArrayPatLen,
132    DuplicateField,
133    PatternArgInExternFn,
134    PrivateAssocItem,
135    PrivateField,
136    RemoveTrailingReturn,
137    RemoveUnnecessaryElse,
138    UnusedMustUse<'db>,
139    ReplaceFilterMapNextWithFindMap,
140    TraitImplIncorrectSafety,
141    TraitImplMissingAssocItems,
142    TraitImplOrphan,
143    TraitImplRedundantAssocItems,
144    TypedHole<'db>,
145    TypeMismatch<'db>,
146    UndeclaredLabel,
147    UnimplementedBuiltinMacro,
148    UnreachableLabel,
149    UnresolvedAssocItem,
150    UnresolvedExternCrate,
151    UnresolvedField<'db>,
152    UnresolvedImport,
153    UnresolvedMacroCall,
154    UnresolvedMethodCall<'db>,
155    UnresolvedModule,
156    UnresolvedIdent,
157    UnusedMut,
158    UnusedVariable,
159    GenericArgsProhibited,
160    ParenthesizedGenericArgsWithoutFnTrait,
161    BadRtn,
162    MissingLifetime,
163    ElidedLifetimesInPath,
164    TypeMustBeKnown<'db>,
165    UnionExprMustHaveExactlyOneField,
166    UnimplementedTrait<'db>,
167];
168
169#[derive(Debug)]
170pub struct BreakOutsideOfLoop {
171    pub expr: InFile<ExprOrPatPtr>,
172    pub is_break: bool,
173    pub bad_value_break: bool,
174}
175
176#[derive(Debug)]
177pub struct TypedHole<'db> {
178    pub expr: InFile<ExprOrPatPtr>,
179    pub expected: Type<'db>,
180}
181
182#[derive(Debug)]
183pub struct UnresolvedModule {
184    pub decl: InFile<AstPtr<ast::Module>>,
185    pub candidates: Box<[String]>,
186}
187
188#[derive(Debug)]
189pub struct UnresolvedExternCrate {
190    pub decl: InFile<AstPtr<ast::ExternCrate>>,
191}
192
193#[derive(Debug)]
194pub struct UnresolvedImport {
195    pub decl: InFile<AstPtr<ast::UseTree>>,
196}
197
198#[derive(Debug, Clone, Eq, PartialEq)]
199pub struct UnresolvedMacroCall {
200    pub range: InFile<TextRange>,
201    pub path: ModPath,
202    pub is_bang: bool,
203}
204#[derive(Debug, Clone, Eq, PartialEq)]
205pub struct UnreachableLabel {
206    pub node: InFile<AstPtr<ast::Lifetime>>,
207    pub name: Name,
208}
209
210#[derive(Debug)]
211pub struct AwaitOutsideOfAsync {
212    pub node: InFile<AstPtr<ast::AwaitExpr>>,
213    pub location: String,
214}
215
216#[derive(Debug, Clone, Eq, PartialEq)]
217pub struct UndeclaredLabel {
218    pub node: InFile<AstPtr<ast::Lifetime>>,
219    pub name: Name,
220}
221
222#[derive(Debug, Clone, Eq, PartialEq)]
223pub struct InactiveCode {
224    pub node: InFile<SyntaxNodePtr>,
225    pub cfg: CfgExpr,
226    pub opts: CfgOptions,
227}
228
229#[derive(Debug, Clone, Eq, PartialEq)]
230pub struct MacroError {
231    pub range: InFile<TextRange>,
232    pub message: String,
233    pub error: bool,
234    pub kind: &'static str,
235}
236
237#[derive(Debug, Clone, Eq, PartialEq)]
238pub struct MacroExpansionParseError {
239    pub range: InFile<TextRange>,
240    pub errors: Arc<[SyntaxError]>,
241}
242
243#[derive(Debug, Clone, Eq, PartialEq)]
244pub struct MacroDefError {
245    pub node: InFile<AstPtr<ast::Macro>>,
246    pub message: String,
247    pub name: Option<TextRange>,
248}
249
250#[derive(Debug)]
251pub struct UnimplementedBuiltinMacro {
252    pub node: InFile<SyntaxNodePtr>,
253}
254
255#[derive(Debug)]
256pub struct InvalidDeriveTarget {
257    pub range: InFile<TextRange>,
258}
259
260#[derive(Debug)]
261pub struct MalformedDerive {
262    pub range: InFile<TextRange>,
263}
264
265#[derive(Debug)]
266pub struct NoSuchField {
267    pub field: InFile<AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>>,
268    pub private: Option<Field>,
269    pub variant: VariantId,
270}
271
272#[derive(Debug)]
273pub struct DuplicateField {
274    pub field: InFile<AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>>,
275    pub variant: Variant,
276}
277
278#[derive(Debug)]
279pub struct PrivateAssocItem {
280    pub expr_or_pat: InFile<ExprOrPatPtr>,
281    pub item: AssocItem,
282}
283
284#[derive(Debug)]
285pub struct MismatchedTupleStructPatArgCount {
286    pub expr_or_pat: InFile<ExprOrPatPtr>,
287    pub expected: usize,
288    pub found: usize,
289}
290
291#[derive(Debug)]
292pub struct MismatchedArrayPatLen {
293    pub pat: InFile<ExprOrPatPtr>,
294    pub expected: u128,
295    pub found: u128,
296    pub has_rest: bool,
297}
298
299#[derive(Debug)]
300pub struct ExpectedFunction<'db> {
301    pub call: InFile<ExprOrPatPtr>,
302    pub found: Type<'db>,
303}
304
305#[derive(Debug)]
306pub struct FunctionalRecordUpdateOnNonStruct {
307    pub base_expr: InFile<ExprOrPatPtr>,
308}
309
310#[derive(Debug)]
311pub struct UnresolvedField<'db> {
312    pub expr: InFile<ExprOrPatPtr>,
313    pub receiver: Type<'db>,
314    pub name: Name,
315    pub method_with_same_name_exists: bool,
316}
317
318#[derive(Debug)]
319pub struct UnresolvedMethodCall<'db> {
320    pub expr: InFile<ExprOrPatPtr>,
321    pub receiver: Type<'db>,
322    pub name: Name,
323    pub field_with_same_name: Option<Type<'db>>,
324    pub assoc_func_with_same_name: Option<Function>,
325}
326
327#[derive(Debug)]
328pub struct UnresolvedAssocItem {
329    pub expr_or_pat: InFile<ExprOrPatPtr>,
330}
331
332#[derive(Debug)]
333pub struct UnresolvedIdent {
334    pub node: InFile<(ExprOrPatPtr, Option<TextRange>)>,
335}
336
337#[derive(Debug)]
338pub struct PrivateField {
339    pub expr: InFile<ExprOrPatPtr>,
340    pub field: Field,
341}
342
343#[derive(Debug, Clone, Copy, PartialEq, Eq)]
344pub enum UnsafeLint {
345    HardError,
346    UnsafeOpInUnsafeFn,
347    DeprecatedSafe2024,
348}
349
350#[derive(Debug)]
351pub struct MissingUnsafe {
352    pub node: InFile<ExprOrPatPtr>,
353    pub lint: UnsafeLint,
354    pub reason: UnsafetyReason,
355}
356
357#[derive(Debug)]
358pub struct MissingFields {
359    pub file: HirFileId,
360    pub field_list_parent: AstPtr<Either<ast::RecordExpr, ast::RecordPat>>,
361    pub field_list_parent_path: Option<AstPtr<ast::Path>>,
362    pub missed_fields: Vec<(Name, Field)>,
363}
364
365#[derive(Debug)]
366pub struct ReplaceFilterMapNextWithFindMap {
367    pub file: HirFileId,
368    /// This expression is the whole method chain up to and including `.filter_map(..).next()`.
369    pub next_expr: AstPtr<ast::Expr>,
370}
371
372#[derive(Debug)]
373pub struct MismatchedArgCount {
374    pub call_expr: InFile<ExprOrPatPtr>,
375    pub expected: usize,
376    pub found: usize,
377}
378
379#[derive(Debug)]
380pub struct MissingMatchArms {
381    pub scrutinee_expr: InFile<AstPtr<ast::Expr>>,
382    pub uncovered_patterns: String,
383}
384
385#[derive(Debug)]
386pub struct NonExhaustiveLet {
387    pub pat: InFile<AstPtr<ast::Pat>>,
388    pub uncovered_patterns: String,
389}
390
391#[derive(Debug)]
392pub struct NonExhaustiveRecordExpr {
393    pub expr: InFile<ExprOrPatPtr>,
394}
395
396#[derive(Debug)]
397pub struct TypeMismatch<'db> {
398    pub expr_or_pat: InFile<ExprOrPatPtr>,
399    pub expected: Type<'db>,
400    pub actual: Type<'db>,
401}
402
403#[derive(Debug)]
404pub struct NeedMut {
405    pub local: Local,
406    pub span: InFile<SyntaxNodePtr>,
407}
408
409#[derive(Debug)]
410pub struct UnusedMut {
411    pub local: Local,
412}
413
414#[derive(Debug)]
415pub struct UnusedVariable {
416    pub local: Local,
417}
418
419#[derive(Debug)]
420pub struct MovedOutOfRef<'db> {
421    pub ty: Type<'db>,
422    pub span: InFile<SyntaxNodePtr>,
423}
424
425#[derive(Debug, PartialEq, Eq)]
426pub struct IncoherentImpl {
427    pub file_id: HirFileId,
428    pub impl_: AstPtr<ast::Impl>,
429}
430
431#[derive(Debug, PartialEq, Eq)]
432pub struct TraitImplOrphan {
433    pub file_id: HirFileId,
434    pub impl_: AstPtr<ast::Impl>,
435}
436
437// FIXME: Split this off into the corresponding 4 rustc errors
438#[derive(Debug, PartialEq, Eq)]
439pub struct TraitImplIncorrectSafety {
440    pub file_id: HirFileId,
441    pub impl_: AstPtr<ast::Impl>,
442    pub should_be_safe: bool,
443}
444
445#[derive(Debug, PartialEq, Eq)]
446pub struct TraitImplMissingAssocItems {
447    pub file_id: HirFileId,
448    pub impl_: AstPtr<ast::Impl>,
449    pub missing: Vec<(Name, AssocItem)>,
450}
451
452#[derive(Debug, PartialEq, Eq)]
453pub struct TraitImplRedundantAssocItems {
454    pub file_id: HirFileId,
455    pub trait_: Trait,
456    pub impl_: AstPtr<ast::Impl>,
457    pub assoc_item: (Name, AssocItem),
458}
459
460#[derive(Debug)]
461pub struct RemoveTrailingReturn {
462    pub return_expr: InFile<AstPtr<ast::ReturnExpr>>,
463}
464
465#[derive(Debug)]
466pub struct RemoveUnnecessaryElse {
467    pub if_expr: InFile<AstPtr<ast::IfExpr>>,
468}
469
470#[derive(Debug)]
471pub struct UnusedMustUse<'db> {
472    pub expr: InFile<ExprOrPatPtr>,
473    pub message: Option<&'db str>,
474}
475
476#[derive(Debug)]
477pub struct CastToUnsized<'db> {
478    pub expr: InFile<ExprOrPatPtr>,
479    pub cast_ty: Type<'db>,
480}
481
482#[derive(Debug)]
483pub struct InvalidCast<'db> {
484    pub expr: InFile<ExprOrPatPtr>,
485    pub error: CastError,
486    pub expr_ty: Type<'db>,
487    pub cast_ty: Type<'db>,
488}
489
490#[derive(Debug)]
491pub struct GenericArgsProhibited {
492    pub args: InFile<AstPtr<Either<ast::GenericArgList, ast::ParenthesizedArgList>>>,
493    pub reason: GenericArgsProhibitedReason,
494}
495
496#[derive(Debug)]
497pub struct ParenthesizedGenericArgsWithoutFnTrait {
498    pub args: InFile<AstPtr<ast::ParenthesizedArgList>>,
499}
500
501#[derive(Debug)]
502pub struct BadRtn {
503    pub rtn: InFile<AstPtr<ast::ReturnTypeSyntax>>,
504}
505
506#[derive(Debug)]
507pub struct IncorrectGenericsLen {
508    /// Points at the name if there are no generics.
509    pub generics_or_segment: InFile<AstPtr<Either<ast::GenericArgList, ast::NameRef>>>,
510    pub kind: IncorrectGenericsLenKind,
511    pub provided: u32,
512    pub expected: u32,
513    pub def: GenericDef,
514}
515
516#[derive(Debug)]
517pub struct MissingLifetime {
518    /// Points at the name if there are no generics.
519    pub generics_or_segment: InFile<AstPtr<Either<ast::GenericArgList, ast::NameRef>>>,
520    pub expected: u32,
521    pub def: GenericDef,
522}
523
524#[derive(Debug)]
525pub struct ElidedLifetimesInPath {
526    /// Points at the name if there are no generics.
527    pub generics_or_segment: InFile<AstPtr<Either<ast::GenericArgList, ast::NameRef>>>,
528    pub expected: u32,
529    pub def: GenericDef,
530    pub hard_error: bool,
531}
532
533#[derive(Debug)]
534pub struct TypeMustBeKnown<'db> {
535    pub at_point: SpanSyntax,
536    pub top_term: Option<Either<Type<'db>, String>>,
537}
538
539#[derive(Debug, Clone, Copy, PartialEq, Eq)]
540pub enum GenericArgKind {
541    Lifetime,
542    Type,
543    Const,
544}
545
546impl GenericArgKind {
547    fn from_id(id: GenericParamId) -> Self {
548        match id {
549            GenericParamId::TypeParamId(_) => GenericArgKind::Type,
550            GenericParamId::ConstParamId(_) => GenericArgKind::Const,
551            GenericParamId::LifetimeParamId(_) => GenericArgKind::Lifetime,
552        }
553    }
554}
555
556#[derive(Debug)]
557pub struct IncorrectGenericsOrder {
558    pub provided_arg: InFile<AstPtr<ast::GenericArg>>,
559    pub expected_kind: GenericArgKind,
560}
561
562#[derive(Debug)]
563pub struct GenericDefaultRefersToSelf {
564    /// The `Self` segment.
565    pub segment: InFile<AstPtr<ast::PathSegment>>,
566}
567
568#[derive(Debug)]
569pub struct UnionExprMustHaveExactlyOneField {
570    pub expr: InFile<ExprOrPatPtr>,
571}
572
573#[derive(Debug)]
574pub struct InvalidLhsOfAssignment {
575    pub lhs: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
576}
577
578#[derive(Debug)]
579pub struct PatternArgInExternFn {
580    pub node: InFile<AstPtr<ast::Pat>>,
581}
582
583#[derive(Debug)]
584pub struct UnimplementedTrait<'db> {
585    pub span: SpanSyntax,
586    pub trait_predicate: crate::TraitPredicate<'db>,
587    pub root_trait_predicate: Option<crate::TraitPredicate<'db>>,
588}
589
590impl<'db> AnyDiagnostic<'db> {
591    pub(crate) fn body_validation_diagnostic(
592        db: &'db dyn HirDatabase,
593        diagnostic: BodyValidationDiagnostic<'db>,
594        source_map: &hir_def::expr_store::BodySourceMap,
595    ) -> Option<AnyDiagnostic<'db>> {
596        match diagnostic {
597            BodyValidationDiagnostic::RecordMissingFields { record, variant, missed_fields } => {
598                let variant_data = variant.fields(db);
599                let missed_fields = missed_fields
600                    .into_iter()
601                    .map(|idx| {
602                        (
603                            variant_data.fields()[idx].name.clone(),
604                            Field { parent: variant.into(), id: idx },
605                        )
606                    })
607                    .collect();
608
609                let record = match record {
610                    Either::Left(record_expr) => source_map.expr_syntax(record_expr).ok()?,
611                    Either::Right(record_pat) => source_map.pat_syntax(record_pat).ok()?,
612                };
613                let file = record.file_id;
614                let root = record.file_syntax(db);
615                match record.value.to_node(&root) {
616                    Either::Left(ast::Expr::RecordExpr(record_expr))
617                        if record_expr.record_expr_field_list().is_some() =>
618                    {
619                        let field_list_parent_path =
620                            record_expr.path().map(|path| AstPtr::new(&path));
621                        return Some(
622                            MissingFields {
623                                file,
624                                field_list_parent: AstPtr::new(&Either::Left(record_expr)),
625                                field_list_parent_path,
626                                missed_fields,
627                            }
628                            .into(),
629                        );
630                    }
631                    Either::Right(ast::Pat::RecordPat(record_pat))
632                        if record_pat.record_pat_field_list().is_some() =>
633                    {
634                        let field_list_parent_path =
635                            record_pat.path().map(|path| AstPtr::new(&path));
636                        return Some(
637                            MissingFields {
638                                file,
639                                field_list_parent: AstPtr::new(&Either::Right(record_pat)),
640                                field_list_parent_path,
641                                missed_fields,
642                            }
643                            .into(),
644                        );
645                    }
646                    _ => {}
647                }
648            }
649            BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { method_call_expr } => {
650                if let Ok(next_source_ptr) = source_map.expr_syntax(method_call_expr) {
651                    return Some(
652                        ReplaceFilterMapNextWithFindMap {
653                            file: next_source_ptr.file_id,
654                            next_expr: next_source_ptr.value.cast()?,
655                        }
656                        .into(),
657                    );
658                }
659            }
660            BodyValidationDiagnostic::MissingMatchArms { match_expr, uncovered_patterns } => {
661                if let Ok(source_ptr) = source_map.expr_syntax(match_expr)
662                    && let root = source_ptr.file_syntax(db)
663                    && let Either::Left(ast::Expr::MatchExpr(match_expr)) =
664                        source_ptr.value.to_node(&root)
665                    && let Some(scrut_expr) = match_expr.expr()
666                    && match_expr.match_arm_list().is_some()
667                {
668                    return Some(
669                        MissingMatchArms {
670                            scrutinee_expr: InFile::new(
671                                source_ptr.file_id,
672                                AstPtr::new(&scrut_expr),
673                            ),
674                            uncovered_patterns,
675                        }
676                        .into(),
677                    );
678                }
679            }
680            BodyValidationDiagnostic::NonExhaustiveLet { pat, uncovered_patterns } => {
681                if let Ok(source_ptr) = source_map.pat_syntax(pat)
682                    && let Some(ast_pat) = source_ptr.value.cast::<ast::Pat>()
683                {
684                    return Some(
685                        NonExhaustiveLet {
686                            pat: InFile::new(source_ptr.file_id, ast_pat),
687                            uncovered_patterns,
688                        }
689                        .into(),
690                    );
691                }
692            }
693            BodyValidationDiagnostic::RemoveTrailingReturn { return_expr } => {
694                if let Ok(source_ptr) = source_map.expr_syntax(return_expr)
695                    // Filters out desugared return expressions (e.g. desugared try operators).
696                    && let Some(ptr) = source_ptr.value.cast::<ast::ReturnExpr>()
697                {
698                    return Some(
699                        RemoveTrailingReturn { return_expr: InFile::new(source_ptr.file_id, ptr) }
700                            .into(),
701                    );
702                }
703            }
704            BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr } => {
705                if let Ok(source_ptr) = source_map.expr_syntax(if_expr)
706                    && let Some(ptr) = source_ptr.value.cast::<ast::IfExpr>()
707                {
708                    return Some(
709                        RemoveUnnecessaryElse { if_expr: InFile::new(source_ptr.file_id, ptr) }
710                            .into(),
711                    );
712                }
713            }
714            BodyValidationDiagnostic::UnusedMustUse { expr, message } => {
715                if let Ok(source_ptr) = source_map.expr_syntax(expr) {
716                    return Some(UnusedMustUse { expr: source_ptr, message }.into());
717                }
718            }
719        }
720        None
721    }
722
723    pub(crate) fn inference_diagnostic(
724        db: &'db dyn HirDatabase,
725        def: DefWithBodyId,
726        d: &'db InferenceDiagnostic,
727        source_map: &hir_def::expr_store::BodySourceMap,
728        sig_map: &hir_def::expr_store::ExpressionStoreSourceMap,
729        env: ParamEnvAndCrate<'db>,
730    ) -> Option<AnyDiagnostic<'db>> {
731        let expr_syntax = |expr| {
732            source_map
733                .expr_syntax(expr)
734                .inspect_err(|_| stdx::never!("inference diagnostic in desugared expr"))
735                .ok()
736        };
737        let pat_syntax = |pat| {
738            source_map
739                .pat_syntax(pat)
740                .inspect_err(|_| stdx::never!("inference diagnostic in desugared pattern"))
741                .ok()
742        };
743        let type_syntax = |pat| {
744            source_map
745                .type_syntax(pat)
746                .inspect_err(|_| stdx::never!("inference diagnostic in desugared type"))
747                .ok()
748        };
749        let expr_or_pat_syntax = |id| match id {
750            ExprOrPatId::ExprId(expr) => expr_syntax(expr),
751            ExprOrPatId::PatId(pat) => pat_syntax(pat),
752        };
753        let span_syntax = |span| match span {
754            hir_ty::Span::ExprId(idx) => expr_syntax(idx).map(|it| it.upcast()),
755            hir_ty::Span::PatId(idx) => pat_syntax(idx).map(|it| it.upcast()),
756            hir_ty::Span::TypeRefId(idx) => type_syntax(idx).map(|it| it.upcast()),
757            hir_ty::Span::BindingId(idx) => {
758                pat_syntax(source_map.patterns_for_binding(idx)[0]).map(|it| it.upcast())
759            }
760            hir_ty::Span::Dummy => {
761                never!("should never create a diagnostic for dummy spans");
762                None
763            }
764        };
765        Some(match d {
766            &InferenceDiagnostic::NoSuchField { field: expr, private, variant } => {
767                let expr_or_pat = match expr {
768                    ExprOrPatId::ExprId(expr) => {
769                        source_map.field_syntax(expr).map(AstPtr::wrap_left)
770                    }
771                    ExprOrPatId::PatId(pat) => source_map.pat_field_syntax(pat),
772                };
773                let private = private.map(|id| Field { id, parent: variant.into() });
774                NoSuchField { field: expr_or_pat, private, variant }.into()
775            }
776            &InferenceDiagnostic::MismatchedArrayPatLen { pat, expected, found, has_rest } => {
777                let pat = pat_syntax(pat)?.map(Into::into);
778                MismatchedArrayPatLen { pat, expected, found, has_rest }.into()
779            }
780            &InferenceDiagnostic::DuplicateField { field: expr, variant } => {
781                let expr_or_pat = match expr {
782                    ExprOrPatId::ExprId(expr) => {
783                        source_map.field_syntax(expr).map(AstPtr::wrap_left)
784                    }
785                    ExprOrPatId::PatId(pat) => source_map.pat_field_syntax(pat),
786                };
787                DuplicateField { field: expr_or_pat, variant: variant.into() }.into()
788            }
789            &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
790                MismatchedArgCount { call_expr: expr_syntax(call_expr)?, expected, found }.into()
791            }
792            &InferenceDiagnostic::PrivateField { expr, field } => {
793                let expr = expr_syntax(expr)?;
794                let field = field.into();
795                PrivateField { expr, field }.into()
796            }
797            &InferenceDiagnostic::PrivateAssocItem { id, item } => {
798                let expr_or_pat = expr_or_pat_syntax(id)?;
799                let item = item.into();
800                PrivateAssocItem { expr_or_pat, item }.into()
801            }
802            InferenceDiagnostic::ExpectedFunction { call_expr, found } => {
803                let call_expr = expr_syntax(*call_expr)?;
804                ExpectedFunction { call: call_expr, found: Type::new(db, def, found.as_ref()) }
805                    .into()
806            }
807            InferenceDiagnostic::UnresolvedField {
808                expr,
809                receiver,
810                name,
811                method_with_same_name_exists,
812            } => {
813                let expr = expr_syntax(*expr)?;
814                UnresolvedField {
815                    expr,
816                    name: name.clone(),
817                    receiver: Type::new(db, def, receiver.as_ref()),
818                    method_with_same_name_exists: *method_with_same_name_exists,
819                }
820                .into()
821            }
822            InferenceDiagnostic::UnresolvedMethodCall {
823                expr,
824                receiver,
825                name,
826                field_with_same_name,
827                assoc_func_with_same_name,
828            } => {
829                let expr = expr_syntax(*expr)?;
830                UnresolvedMethodCall {
831                    expr,
832                    name: name.clone(),
833                    receiver: Type::new(db, def, receiver.as_ref()),
834                    field_with_same_name: field_with_same_name
835                        .as_ref()
836                        .map(|ty| Type::new(db, def, ty.as_ref())),
837                    assoc_func_with_same_name: assoc_func_with_same_name.map(Into::into),
838                }
839                .into()
840            }
841            &InferenceDiagnostic::UnresolvedAssocItem { id } => {
842                let expr_or_pat = expr_or_pat_syntax(id)?;
843                UnresolvedAssocItem { expr_or_pat }.into()
844            }
845            &InferenceDiagnostic::UnresolvedIdent { id } => {
846                let node = match id {
847                    ExprOrPatId::ExprId(id) => match source_map.expr_syntax(id) {
848                        Ok(syntax) => syntax.map(|it| (it, None)),
849                        Err(SyntheticSyntax) => source_map
850                            .format_args_implicit_capture(id)?
851                            .map(|(node, range)| (node.wrap_left(), Some(range))),
852                    },
853                    ExprOrPatId::PatId(id) => pat_syntax(id)?.map(|it| (it, None)),
854                };
855                UnresolvedIdent { node }.into()
856            }
857            &InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
858                let expr = expr_syntax(expr)?;
859                BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()
860            }
861            &InferenceDiagnostic::NonExhaustiveRecordExpr { expr } => {
862                NonExhaustiveRecordExpr { expr: expr_syntax(expr)? }.into()
863            }
864            &InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr } => {
865                FunctionalRecordUpdateOnNonStruct { base_expr: expr_syntax(base_expr)? }.into()
866            }
867            InferenceDiagnostic::TypedHole { expr, expected } => {
868                let expr = expr_syntax(*expr)?;
869                TypedHole { expr, expected: Type::new(db, def, expected.as_ref()) }.into()
870            }
871            &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => {
872                let InFile { file_id, value } = pat_syntax(pat)?;
873                // cast from Either<Pat, SelfParam> -> Either<_, Pat>
874                let ptr = AstPtr::try_from_raw(value.syntax_node_ptr())?;
875                let expr_or_pat = InFile { file_id, value: ptr };
876                MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into()
877            }
878            InferenceDiagnostic::CastToUnsized { expr, cast_ty } => {
879                let expr = expr_syntax(*expr)?;
880                CastToUnsized { expr, cast_ty: Type::new(db, def, cast_ty.as_ref()) }.into()
881            }
882            InferenceDiagnostic::InvalidCast { expr, error, expr_ty, cast_ty } => {
883                let expr = expr_syntax(*expr)?;
884                let expr_ty = Type::new(db, def, expr_ty.as_ref());
885                let cast_ty = Type::new(db, def, cast_ty.as_ref());
886                InvalidCast { expr, error: *error, expr_ty, cast_ty }.into()
887            }
888            InferenceDiagnostic::TyDiagnostic { source, diag } => {
889                let source_map = match source {
890                    InferenceTyDiagnosticSource::Body => source_map,
891                    InferenceTyDiagnosticSource::Signature => sig_map,
892                };
893                Self::ty_diagnostic(diag, source_map, db)?
894            }
895            InferenceDiagnostic::PathDiagnostic { node, diag } => {
896                let source = expr_or_pat_syntax(*node)?;
897                let syntax = source.value.to_node(&db.parse_or_expand(source.file_id));
898                let path = match_ast! {
899                    match (syntax.syntax()) {
900                        ast::RecordExpr(it) => it.path()?,
901                        ast::RecordPat(it) => it.path()?,
902                        ast::TupleStructPat(it) => it.path()?,
903                        ast::PathExpr(it) => it.path()?,
904                        ast::PathPat(it) => it.path()?,
905                        _ => return None,
906                    }
907                };
908                Self::path_diagnostic(diag, source.with_value(path))?
909            }
910            &InferenceDiagnostic::MethodCallIncorrectGenericsLen {
911                expr,
912                provided_count,
913                expected_count,
914                kind,
915                def,
916            } => {
917                let syntax = expr_syntax(expr)?;
918                let file_id = syntax.file_id;
919                let syntax =
920                    syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
921                let generics_or_name = syntax
922                    .generic_arg_list()
923                    .map(Either::Left)
924                    .or_else(|| syntax.name_ref().map(Either::Right))?;
925                let generics_or_name = InFile::new(file_id, AstPtr::new(&generics_or_name));
926                IncorrectGenericsLen {
927                    generics_or_segment: generics_or_name,
928                    kind,
929                    provided: provided_count,
930                    expected: expected_count,
931                    def: def.into(),
932                }
933                .into()
934            }
935            &InferenceDiagnostic::MethodCallIncorrectGenericsOrder {
936                expr,
937                param_id,
938                arg_idx,
939                has_self_arg,
940            } => {
941                let syntax = expr_syntax(expr)?;
942                let file_id = syntax.file_id;
943                let syntax =
944                    syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
945                let generic_args = syntax.generic_arg_list()?;
946                let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
947                let provided_arg = InFile::new(file_id, AstPtr::new(&provided_arg));
948                let expected_kind = GenericArgKind::from_id(param_id);
949                IncorrectGenericsOrder { provided_arg, expected_kind }.into()
950            }
951            &InferenceDiagnostic::InvalidLhsOfAssignment { lhs } => {
952                let lhs = expr_syntax(lhs)?;
953                InvalidLhsOfAssignment { lhs }.into()
954            }
955            &InferenceDiagnostic::TypeMustBeKnown { at_point, ref top_term } => {
956                let at_point = span_syntax(at_point)?;
957                let top_term = top_term.as_ref().map(|top_term| match top_term.as_ref().kind() {
958                    rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(Type {
959                        ty,
960                        env: crate::body_param_env_from_has_crate(db, def),
961                    }),
962                    // FIXME: Printing the const to string is definitely not the correct thing to do here.
963                    rustc_type_ir::GenericArgKind::Const(konst) => Either::Right(
964                        konst.display(db, DisplayTarget::from_crate(db, def.krate(db))).to_string(),
965                    ),
966                    rustc_type_ir::GenericArgKind::Lifetime(_) => {
967                        unreachable!("we currently don't emit TypeMustBeKnown for lifetimes")
968                    }
969                });
970                TypeMustBeKnown { at_point, top_term }.into()
971            }
972            &InferenceDiagnostic::UnionExprMustHaveExactlyOneField { expr } => {
973                let expr = expr_syntax(expr)?;
974                UnionExprMustHaveExactlyOneField { expr }.into()
975            }
976            InferenceDiagnostic::TypeMismatch { node, expected, found } => {
977                let expr_or_pat = expr_or_pat_syntax(*node)?;
978                TypeMismatch {
979                    expr_or_pat,
980                    expected: Type { env, ty: expected.as_ref() },
981                    actual: Type { env, ty: found.as_ref() },
982                }
983                .into()
984            }
985            InferenceDiagnostic::SolverDiagnostic(d) => {
986                let span = span_syntax(d.span)?;
987                Self::solver_diagnostic(db, &d.kind, span, env)?
988            }
989        })
990    }
991
992    fn solver_diagnostic(
993        db: &'db dyn HirDatabase,
994        d: &'db SolverDiagnosticKind,
995        span: SpanSyntax,
996        env: ParamEnvAndCrate<'db>,
997    ) -> Option<AnyDiagnostic<'db>> {
998        let interner = DbInterner::new_no_crate(db);
999        Some(match d {
1000            SolverDiagnosticKind::TraitUnimplemented { trait_predicate, root_trait_predicate } => {
1001                let trait_predicate =
1002                    crate::TraitPredicate { inner: trait_predicate.get(interner), env };
1003                let root_trait_predicate =
1004                    root_trait_predicate.as_ref().map(|root_trait_predicate| {
1005                        crate::TraitPredicate { inner: root_trait_predicate.get(interner), env }
1006                    });
1007                UnimplementedTrait { span, trait_predicate, root_trait_predicate }.into()
1008            }
1009        })
1010    }
1011
1012    fn path_diagnostic(
1013        diag: &PathLoweringDiagnostic,
1014        path: InFile<ast::Path>,
1015    ) -> Option<AnyDiagnostic<'db>> {
1016        Some(match *diag {
1017            PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => {
1018                let segment = hir_segment_to_ast_segment(&path.value, segment)?;
1019
1020                if let Some(rtn) = segment.return_type_syntax() {
1021                    // RTN errors are emitted as `GenericArgsProhibited` or `ParenthesizedGenericArgsWithoutFnTrait`.
1022                    return Some(BadRtn { rtn: path.with_value(AstPtr::new(&rtn)) }.into());
1023                }
1024
1025                let args = if let Some(generics) = segment.generic_arg_list() {
1026                    AstPtr::new(&generics).wrap_left()
1027                } else {
1028                    AstPtr::new(&segment.parenthesized_arg_list()?).wrap_right()
1029                };
1030                let args = path.with_value(args);
1031                GenericArgsProhibited { args, reason }.into()
1032            }
1033            PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment } => {
1034                let segment = hir_segment_to_ast_segment(&path.value, segment)?;
1035
1036                if let Some(rtn) = segment.return_type_syntax() {
1037                    // RTN errors are emitted as `GenericArgsProhibited` or `ParenthesizedGenericArgsWithoutFnTrait`.
1038                    return Some(BadRtn { rtn: path.with_value(AstPtr::new(&rtn)) }.into());
1039                }
1040
1041                let args = AstPtr::new(&segment.parenthesized_arg_list()?);
1042                let args = path.with_value(args);
1043                ParenthesizedGenericArgsWithoutFnTrait { args }.into()
1044            }
1045            PathLoweringDiagnostic::IncorrectGenericsLen {
1046                generics_source,
1047                provided_count,
1048                expected_count,
1049                kind,
1050                def,
1051            } => {
1052                let generics_or_segment =
1053                    path_generics_source_to_ast(&path.value, generics_source)?;
1054                let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
1055                IncorrectGenericsLen {
1056                    generics_or_segment,
1057                    kind,
1058                    provided: provided_count,
1059                    expected: expected_count,
1060                    def: def.into(),
1061                }
1062                .into()
1063            }
1064            PathLoweringDiagnostic::IncorrectGenericsOrder {
1065                generics_source,
1066                param_id,
1067                arg_idx,
1068                has_self_arg,
1069            } => {
1070                let generic_args =
1071                    path_generics_source_to_ast(&path.value, generics_source)?.left()?;
1072                let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
1073                let provided_arg = path.with_value(AstPtr::new(&provided_arg));
1074                let expected_kind = GenericArgKind::from_id(param_id);
1075                IncorrectGenericsOrder { provided_arg, expected_kind }.into()
1076            }
1077            PathLoweringDiagnostic::MissingLifetime { generics_source, expected_count, def }
1078            | PathLoweringDiagnostic::ElisionFailure { generics_source, expected_count, def } => {
1079                let generics_or_segment =
1080                    path_generics_source_to_ast(&path.value, generics_source)?;
1081                let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
1082                MissingLifetime { generics_or_segment, expected: expected_count, def: def.into() }
1083                    .into()
1084            }
1085            PathLoweringDiagnostic::ElidedLifetimesInPath {
1086                generics_source,
1087                expected_count,
1088                def,
1089                hard_error,
1090            } => {
1091                let generics_or_segment =
1092                    path_generics_source_to_ast(&path.value, generics_source)?;
1093                let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
1094                ElidedLifetimesInPath {
1095                    generics_or_segment,
1096                    expected: expected_count,
1097                    def: def.into(),
1098                    hard_error,
1099                }
1100                .into()
1101            }
1102            PathLoweringDiagnostic::GenericDefaultRefersToSelf { segment } => {
1103                let segment = hir_segment_to_ast_segment(&path.value, segment)?;
1104                let segment = path.with_value(AstPtr::new(&segment));
1105                GenericDefaultRefersToSelf { segment }.into()
1106            }
1107        })
1108    }
1109
1110    pub(crate) fn ty_diagnostic(
1111        diag: &TyLoweringDiagnostic,
1112        source_map: &ExpressionStoreSourceMap,
1113        db: &'db dyn HirDatabase,
1114    ) -> Option<AnyDiagnostic<'db>> {
1115        let Ok(source) = source_map.type_syntax(diag.source) else {
1116            stdx::never!("error on synthetic type syntax");
1117            return None;
1118        };
1119        let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id));
1120        Some(match &diag.kind {
1121            TyLoweringDiagnosticKind::PathDiagnostic(diag) => {
1122                let ast::Type::PathType(syntax) = syntax() else { return None };
1123                Self::path_diagnostic(diag, source.with_value(syntax.path()?))?
1124            }
1125        })
1126    }
1127}
1128
1129fn path_generics_source_to_ast(
1130    path: &ast::Path,
1131    generics_source: PathGenericsSource,
1132) -> Option<Either<ast::GenericArgList, ast::NameRef>> {
1133    Some(match generics_source {
1134        PathGenericsSource::Segment(segment) => {
1135            let segment = hir_segment_to_ast_segment(path, segment)?;
1136            segment
1137                .generic_arg_list()
1138                .map(Either::Left)
1139                .or_else(|| segment.name_ref().map(Either::Right))?
1140        }
1141        PathGenericsSource::AssocType { segment, assoc_type } => {
1142            let segment = hir_segment_to_ast_segment(path, segment)?;
1143            let segment_args = segment.generic_arg_list()?;
1144            let assoc = hir_assoc_type_binding_to_ast(&segment_args, assoc_type)?;
1145            assoc
1146                .generic_arg_list()
1147                .map(Either::Left)
1148                .or_else(|| assoc.name_ref().map(Either::Right))?
1149        }
1150    })
1151}