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