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