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, 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 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#[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 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 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 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 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 && 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 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 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 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 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}