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