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