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, Field)>,
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| {
480 (
481 variant_data.fields()[idx].name.clone(),
482 Field { parent: variant.into(), id: idx },
483 )
484 })
485 .collect();
486
487 let record = match record {
488 Either::Left(record_expr) => source_map.expr_syntax(record_expr).ok()?,
489 Either::Right(record_pat) => source_map.pat_syntax(record_pat).ok()?,
490 };
491 let file = record.file_id;
492 let root = record.file_syntax(db);
493 match record.value.to_node(&root) {
494 Either::Left(ast::Expr::RecordExpr(record_expr))
495 if record_expr.record_expr_field_list().is_some() =>
496 {
497 let field_list_parent_path =
498 record_expr.path().map(|path| AstPtr::new(&path));
499 return Some(
500 MissingFields {
501 file,
502 field_list_parent: AstPtr::new(&Either::Left(record_expr)),
503 field_list_parent_path,
504 missed_fields,
505 }
506 .into(),
507 );
508 }
509 Either::Right(ast::Pat::RecordPat(record_pat))
510 if record_pat.record_pat_field_list().is_some() =>
511 {
512 let field_list_parent_path =
513 record_pat.path().map(|path| AstPtr::new(&path));
514 return Some(
515 MissingFields {
516 file,
517 field_list_parent: AstPtr::new(&Either::Right(record_pat)),
518 field_list_parent_path,
519 missed_fields,
520 }
521 .into(),
522 );
523 }
524 _ => {}
525 }
526 }
527 BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { method_call_expr } => {
528 if let Ok(next_source_ptr) = source_map.expr_syntax(method_call_expr) {
529 return Some(
530 ReplaceFilterMapNextWithFindMap {
531 file: next_source_ptr.file_id,
532 next_expr: next_source_ptr.value.cast()?,
533 }
534 .into(),
535 );
536 }
537 }
538 BodyValidationDiagnostic::MissingMatchArms { match_expr, uncovered_patterns } => {
539 match source_map.expr_syntax(match_expr) {
540 Ok(source_ptr) => {
541 let root = source_ptr.file_syntax(db);
542 if let Either::Left(ast::Expr::MatchExpr(match_expr)) =
543 &source_ptr.value.to_node(&root)
544 {
545 match match_expr.expr() {
546 Some(scrut_expr) if match_expr.match_arm_list().is_some() => {
547 return Some(
548 MissingMatchArms {
549 scrutinee_expr: InFile::new(
550 source_ptr.file_id,
551 AstPtr::new(&scrut_expr),
552 ),
553 uncovered_patterns,
554 }
555 .into(),
556 );
557 }
558 _ => {}
559 }
560 }
561 }
562 Err(SyntheticSyntax) => (),
563 }
564 }
565 BodyValidationDiagnostic::NonExhaustiveLet { pat, uncovered_patterns } => {
566 match source_map.pat_syntax(pat) {
567 Ok(source_ptr) => {
568 if let Some(ast_pat) = source_ptr.value.cast::<ast::Pat>() {
569 return Some(
570 NonExhaustiveLet {
571 pat: InFile::new(source_ptr.file_id, ast_pat),
572 uncovered_patterns,
573 }
574 .into(),
575 );
576 }
577 }
578 Err(SyntheticSyntax) => {}
579 }
580 }
581 BodyValidationDiagnostic::RemoveTrailingReturn { return_expr } => {
582 if let Ok(source_ptr) = source_map.expr_syntax(return_expr) {
583 if let Some(ptr) = source_ptr.value.cast::<ast::ReturnExpr>() {
585 return Some(
586 RemoveTrailingReturn {
587 return_expr: InFile::new(source_ptr.file_id, ptr),
588 }
589 .into(),
590 );
591 }
592 }
593 }
594 BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr } => {
595 if let Ok(source_ptr) = source_map.expr_syntax(if_expr)
596 && let Some(ptr) = source_ptr.value.cast::<ast::IfExpr>()
597 {
598 return Some(
599 RemoveUnnecessaryElse { if_expr: InFile::new(source_ptr.file_id, ptr) }
600 .into(),
601 );
602 }
603 }
604 }
605 None
606 }
607
608 pub(crate) fn inference_diagnostic(
609 db: &'db dyn HirDatabase,
610 def: DefWithBodyId,
611 d: &InferenceDiagnostic,
612 source_map: &hir_def::expr_store::BodySourceMap,
613 sig_map: &hir_def::expr_store::ExpressionStoreSourceMap,
614 ) -> Option<AnyDiagnostic<'db>> {
615 let expr_syntax = |expr| {
616 source_map
617 .expr_syntax(expr)
618 .inspect_err(|_| stdx::never!("inference diagnostic in desugared expr"))
619 .ok()
620 };
621 let pat_syntax = |pat| {
622 source_map
623 .pat_syntax(pat)
624 .inspect_err(|_| stdx::never!("inference diagnostic in desugared pattern"))
625 .ok()
626 };
627 let expr_or_pat_syntax = |id| match id {
628 ExprOrPatId::ExprId(expr) => expr_syntax(expr),
629 ExprOrPatId::PatId(pat) => pat_syntax(pat),
630 };
631 Some(match d {
632 &InferenceDiagnostic::NoSuchField { field: expr, private, variant } => {
633 let expr_or_pat = match expr {
634 ExprOrPatId::ExprId(expr) => {
635 source_map.field_syntax(expr).map(AstPtr::wrap_left)
636 }
637 ExprOrPatId::PatId(pat) => source_map.pat_field_syntax(pat),
638 };
639 let private = private.map(|id| Field { id, parent: variant.into() });
640 NoSuchField { field: expr_or_pat, private, variant }.into()
641 }
642 &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
643 MismatchedArgCount { call_expr: expr_syntax(call_expr)?, expected, found }.into()
644 }
645 &InferenceDiagnostic::PrivateField { expr, field } => {
646 let expr = expr_syntax(expr)?;
647 let field = field.into();
648 PrivateField { expr, field }.into()
649 }
650 &InferenceDiagnostic::PrivateAssocItem { id, item } => {
651 let expr_or_pat = expr_or_pat_syntax(id)?;
652 let item = item.into();
653 PrivateAssocItem { expr_or_pat, item }.into()
654 }
655 InferenceDiagnostic::ExpectedFunction { call_expr, found } => {
656 let call_expr = expr_syntax(*call_expr)?;
657 ExpectedFunction { call: call_expr, found: Type::new(db, def, found.as_ref()) }
658 .into()
659 }
660 InferenceDiagnostic::UnresolvedField {
661 expr,
662 receiver,
663 name,
664 method_with_same_name_exists,
665 } => {
666 let expr = expr_syntax(*expr)?;
667 UnresolvedField {
668 expr,
669 name: name.clone(),
670 receiver: Type::new(db, def, receiver.as_ref()),
671 method_with_same_name_exists: *method_with_same_name_exists,
672 }
673 .into()
674 }
675 InferenceDiagnostic::UnresolvedMethodCall {
676 expr,
677 receiver,
678 name,
679 field_with_same_name,
680 assoc_func_with_same_name,
681 } => {
682 let expr = expr_syntax(*expr)?;
683 UnresolvedMethodCall {
684 expr,
685 name: name.clone(),
686 receiver: Type::new(db, def, receiver.as_ref()),
687 field_with_same_name: field_with_same_name
688 .as_ref()
689 .map(|ty| Type::new(db, def, ty.as_ref())),
690 assoc_func_with_same_name: assoc_func_with_same_name.map(Into::into),
691 }
692 .into()
693 }
694 &InferenceDiagnostic::UnresolvedAssocItem { id } => {
695 let expr_or_pat = expr_or_pat_syntax(id)?;
696 UnresolvedAssocItem { expr_or_pat }.into()
697 }
698 &InferenceDiagnostic::UnresolvedIdent { id } => {
699 let node = match id {
700 ExprOrPatId::ExprId(id) => match source_map.expr_syntax(id) {
701 Ok(syntax) => syntax.map(|it| (it, None)),
702 Err(SyntheticSyntax) => source_map
703 .format_args_implicit_capture(id)?
704 .map(|(node, range)| (node.wrap_left(), Some(range))),
705 },
706 ExprOrPatId::PatId(id) => pat_syntax(id)?.map(|it| (it, None)),
707 };
708 UnresolvedIdent { node }.into()
709 }
710 &InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
711 let expr = expr_syntax(expr)?;
712 BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()
713 }
714 InferenceDiagnostic::TypedHole { expr, expected } => {
715 let expr = expr_syntax(*expr)?;
716 TypedHole { expr, expected: Type::new(db, def, expected.as_ref()) }.into()
717 }
718 &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => {
719 let expr_or_pat = match pat {
720 ExprOrPatId::ExprId(expr) => expr_syntax(expr)?,
721 ExprOrPatId::PatId(pat) => {
722 let InFile { file_id, value } = pat_syntax(pat)?;
723
724 let ptr = AstPtr::try_from_raw(value.syntax_node_ptr())?;
726 InFile { file_id, value: ptr }
727 }
728 };
729 MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into()
730 }
731 InferenceDiagnostic::CastToUnsized { expr, cast_ty } => {
732 let expr = expr_syntax(*expr)?;
733 CastToUnsized { expr, cast_ty: Type::new(db, def, cast_ty.as_ref()) }.into()
734 }
735 InferenceDiagnostic::InvalidCast { expr, error, expr_ty, cast_ty } => {
736 let expr = expr_syntax(*expr)?;
737 let expr_ty = Type::new(db, def, expr_ty.as_ref());
738 let cast_ty = Type::new(db, def, cast_ty.as_ref());
739 InvalidCast { expr, error: *error, expr_ty, cast_ty }.into()
740 }
741 InferenceDiagnostic::TyDiagnostic { source, diag } => {
742 let source_map = match source {
743 InferenceTyDiagnosticSource::Body => source_map,
744 InferenceTyDiagnosticSource::Signature => sig_map,
745 };
746 Self::ty_diagnostic(diag, source_map, db)?
747 }
748 InferenceDiagnostic::PathDiagnostic { node, diag } => {
749 let source = expr_or_pat_syntax(*node)?;
750 let syntax = source.value.to_node(&db.parse_or_expand(source.file_id));
751 let path = match_ast! {
752 match (syntax.syntax()) {
753 ast::RecordExpr(it) => it.path()?,
754 ast::RecordPat(it) => it.path()?,
755 ast::TupleStructPat(it) => it.path()?,
756 ast::PathExpr(it) => it.path()?,
757 ast::PathPat(it) => it.path()?,
758 _ => return None,
759 }
760 };
761 Self::path_diagnostic(diag, source.with_value(path))?
762 }
763 &InferenceDiagnostic::MethodCallIncorrectGenericsLen {
764 expr,
765 provided_count,
766 expected_count,
767 kind,
768 def,
769 } => {
770 let syntax = expr_syntax(expr)?;
771 let file_id = syntax.file_id;
772 let syntax =
773 syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
774 let generics_or_name = syntax
775 .generic_arg_list()
776 .map(Either::Left)
777 .or_else(|| syntax.name_ref().map(Either::Right))?;
778 let generics_or_name = InFile::new(file_id, AstPtr::new(&generics_or_name));
779 IncorrectGenericsLen {
780 generics_or_segment: generics_or_name,
781 kind,
782 provided: provided_count,
783 expected: expected_count,
784 def: def.into(),
785 }
786 .into()
787 }
788 &InferenceDiagnostic::MethodCallIncorrectGenericsOrder {
789 expr,
790 param_id,
791 arg_idx,
792 has_self_arg,
793 } => {
794 let syntax = expr_syntax(expr)?;
795 let file_id = syntax.file_id;
796 let syntax =
797 syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
798 let generic_args = syntax.generic_arg_list()?;
799 let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
800 let provided_arg = InFile::new(file_id, AstPtr::new(&provided_arg));
801 let expected_kind = GenericArgKind::from_id(param_id);
802 IncorrectGenericsOrder { provided_arg, expected_kind }.into()
803 }
804 })
805 }
806
807 fn path_diagnostic(
808 diag: &PathLoweringDiagnostic,
809 path: InFile<ast::Path>,
810 ) -> Option<AnyDiagnostic<'db>> {
811 Some(match *diag {
812 PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => {
813 let segment = hir_segment_to_ast_segment(&path.value, segment)?;
814
815 if let Some(rtn) = segment.return_type_syntax() {
816 return Some(BadRtn { rtn: path.with_value(AstPtr::new(&rtn)) }.into());
818 }
819
820 let args = if let Some(generics) = segment.generic_arg_list() {
821 AstPtr::new(&generics).wrap_left()
822 } else {
823 AstPtr::new(&segment.parenthesized_arg_list()?).wrap_right()
824 };
825 let args = path.with_value(args);
826 GenericArgsProhibited { args, reason }.into()
827 }
828 PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment } => {
829 let segment = hir_segment_to_ast_segment(&path.value, segment)?;
830
831 if let Some(rtn) = segment.return_type_syntax() {
832 return Some(BadRtn { rtn: path.with_value(AstPtr::new(&rtn)) }.into());
834 }
835
836 let args = AstPtr::new(&segment.parenthesized_arg_list()?);
837 let args = path.with_value(args);
838 ParenthesizedGenericArgsWithoutFnTrait { args }.into()
839 }
840 PathLoweringDiagnostic::IncorrectGenericsLen {
841 generics_source,
842 provided_count,
843 expected_count,
844 kind,
845 def,
846 } => {
847 let generics_or_segment =
848 path_generics_source_to_ast(&path.value, generics_source)?;
849 let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
850 IncorrectGenericsLen {
851 generics_or_segment,
852 kind,
853 provided: provided_count,
854 expected: expected_count,
855 def: def.into(),
856 }
857 .into()
858 }
859 PathLoweringDiagnostic::IncorrectGenericsOrder {
860 generics_source,
861 param_id,
862 arg_idx,
863 has_self_arg,
864 } => {
865 let generic_args =
866 path_generics_source_to_ast(&path.value, generics_source)?.left()?;
867 let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
868 let provided_arg = path.with_value(AstPtr::new(&provided_arg));
869 let expected_kind = GenericArgKind::from_id(param_id);
870 IncorrectGenericsOrder { provided_arg, expected_kind }.into()
871 }
872 PathLoweringDiagnostic::MissingLifetime { generics_source, expected_count, def }
873 | PathLoweringDiagnostic::ElisionFailure { generics_source, expected_count, def } => {
874 let generics_or_segment =
875 path_generics_source_to_ast(&path.value, generics_source)?;
876 let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
877 MissingLifetime { generics_or_segment, expected: expected_count, def: def.into() }
878 .into()
879 }
880 PathLoweringDiagnostic::ElidedLifetimesInPath {
881 generics_source,
882 expected_count,
883 def,
884 hard_error,
885 } => {
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 ElidedLifetimesInPath {
890 generics_or_segment,
891 expected: expected_count,
892 def: def.into(),
893 hard_error,
894 }
895 .into()
896 }
897 })
898 }
899
900 pub(crate) fn ty_diagnostic(
901 diag: &TyLoweringDiagnostic,
902 source_map: &ExpressionStoreSourceMap,
903 db: &'db dyn HirDatabase,
904 ) -> Option<AnyDiagnostic<'db>> {
905 let Ok(source) = source_map.type_syntax(diag.source) else {
906 stdx::never!("error on synthetic type syntax");
907 return None;
908 };
909 let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id));
910 Some(match &diag.kind {
911 TyLoweringDiagnosticKind::PathDiagnostic(diag) => {
912 let ast::Type::PathType(syntax) = syntax() else { return None };
913 Self::path_diagnostic(diag, source.with_value(syntax.path()?))?
914 }
915 })
916 }
917}
918
919fn path_generics_source_to_ast(
920 path: &ast::Path,
921 generics_source: PathGenericsSource,
922) -> Option<Either<ast::GenericArgList, ast::NameRef>> {
923 Some(match generics_source {
924 PathGenericsSource::Segment(segment) => {
925 let segment = hir_segment_to_ast_segment(path, segment)?;
926 segment
927 .generic_arg_list()
928 .map(Either::Left)
929 .or_else(|| segment.name_ref().map(Either::Right))?
930 }
931 PathGenericsSource::AssocType { segment, assoc_type } => {
932 let segment = hir_segment_to_ast_segment(path, segment)?;
933 let segment_args = segment.generic_arg_list()?;
934 let assoc = hir_assoc_type_binding_to_ast(&segment_args, assoc_type)?;
935 assoc
936 .generic_arg_list()
937 .map(Either::Left)
938 .or_else(|| assoc.name_ref().map(Either::Right))?
939 }
940 })
941}