Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/diagnostics.rs')
-rw-r--r--crates/hir/src/diagnostics.rs237
1 files changed, 234 insertions, 3 deletions
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index bf29a53913..2d8f1dbad5 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -4,11 +4,12 @@
//! This probably isn't the best way to do this -- ideally, diagnostics should
//! be expressed in terms of hir types themselves.
pub use hir_ty::diagnostics::{CaseType, IncorrectCase};
+use hir_ty::{db::HirDatabase, diagnostics::BodyValidationDiagnostic, InferenceDiagnostic};
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
use either::Either;
-use hir_def::{path::ModPath, AssocItemId};
+use hir_def::{body::SyntheticSyntax, hir::ExprOrPatId, path::ModPath, AssocItemId, DefWithBodyId};
use hir_expand::{name::Name, HirFileId, InFile};
use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange};
@@ -30,14 +31,28 @@ macro_rules! diagnostics {
)*
};
}
+// FIXME Accept something like the following in the macro call instead
+// diagnostics![
+// pub struct BreakOutsideOfLoop {
+// pub expr: InFile<AstPtr<ast::Expr>>,
+// pub is_break: bool,
+// pub bad_value_break: bool,
+// }, ...
+// or more concisely
+// BreakOutsideOfLoop {
+// expr: InFile<AstPtr<ast::Expr>>,
+// is_break: bool,
+// bad_value_break: bool,
+// }, ...
+// ]
diagnostics![
BreakOutsideOfLoop,
ExpectedFunction,
InactiveCode,
+ IncoherentImpl,
IncorrectCase,
InvalidDeriveTarget,
- IncoherentImpl,
MacroDefError,
MacroError,
MacroExpansionParseError,
@@ -55,8 +70,8 @@ diagnostics![
ReplaceFilterMapNextWithFindMap,
TraitImplIncorrectSafety,
TraitImplMissingAssocItems,
- TraitImplRedundantAssocItems,
TraitImplOrphan,
+ TraitImplRedundantAssocItems,
TypedHole,
TypeMismatch,
UndeclaredLabel,
@@ -326,3 +341,219 @@ pub struct TraitImplRedundantAssocItems {
pub impl_: AstPtr<ast::Impl>,
pub assoc_item: (Name, AssocItem),
}
+
+impl AnyDiagnostic {
+ pub(crate) fn body_validation_diagnostic(
+ db: &dyn HirDatabase,
+ diagnostic: BodyValidationDiagnostic,
+ source_map: &hir_def::body::BodySourceMap,
+ ) -> Option<AnyDiagnostic> {
+ match diagnostic {
+ BodyValidationDiagnostic::RecordMissingFields { record, variant, missed_fields } => {
+ let variant_data = variant.variant_data(db.upcast());
+ let missed_fields = missed_fields
+ .into_iter()
+ .map(|idx| variant_data.fields()[idx].name.clone())
+ .collect();
+
+ match record {
+ Either::Left(record_expr) => match source_map.expr_syntax(record_expr) {
+ Ok(source_ptr) => {
+ let root = source_ptr.file_syntax(db.upcast());
+ if let ast::Expr::RecordExpr(record_expr) =
+ source_ptr.value.to_node(&root)
+ {
+ if record_expr.record_expr_field_list().is_some() {
+ let field_list_parent_path =
+ record_expr.path().map(|path| AstPtr::new(&path));
+ return Some(
+ MissingFields {
+ file: source_ptr.file_id,
+ field_list_parent: AstPtr::new(&Either::Left(
+ record_expr,
+ )),
+ field_list_parent_path,
+ missed_fields,
+ }
+ .into(),
+ );
+ }
+ }
+ }
+ Err(SyntheticSyntax) => (),
+ },
+ Either::Right(record_pat) => match source_map.pat_syntax(record_pat) {
+ Ok(source_ptr) => {
+ if let Some(ptr) = source_ptr.value.cast::<ast::RecordPat>() {
+ let root = source_ptr.file_syntax(db.upcast());
+ let record_pat = ptr.to_node(&root);
+ if record_pat.record_pat_field_list().is_some() {
+ let field_list_parent_path =
+ record_pat.path().map(|path| AstPtr::new(&path));
+ return Some(
+ MissingFields {
+ file: source_ptr.file_id,
+ field_list_parent: AstPtr::new(&Either::Right(
+ record_pat,
+ )),
+ field_list_parent_path,
+ missed_fields,
+ }
+ .into(),
+ );
+ }
+ }
+ }
+ Err(SyntheticSyntax) => (),
+ },
+ }
+ }
+ BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { method_call_expr } => {
+ if let Ok(next_source_ptr) = source_map.expr_syntax(method_call_expr) {
+ return Some(
+ ReplaceFilterMapNextWithFindMap {
+ file: next_source_ptr.file_id,
+ next_expr: next_source_ptr.value,
+ }
+ .into(),
+ );
+ }
+ }
+ BodyValidationDiagnostic::MissingMatchArms { match_expr, uncovered_patterns } => {
+ match source_map.expr_syntax(match_expr) {
+ Ok(source_ptr) => {
+ let root = source_ptr.file_syntax(db.upcast());
+ if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) {
+ match match_expr.expr() {
+ Some(scrut_expr) if match_expr.match_arm_list().is_some() => {
+ return Some(
+ MissingMatchArms {
+ scrutinee_expr: InFile::new(
+ source_ptr.file_id,
+ AstPtr::new(&scrut_expr),
+ ),
+ uncovered_patterns,
+ }
+ .into(),
+ );
+ }
+ _ => {}
+ }
+ }
+ }
+ Err(SyntheticSyntax) => (),
+ }
+ }
+ }
+ None
+ }
+
+ pub(crate) fn inference_diagnostic(
+ db: &dyn HirDatabase,
+ def: DefWithBodyId,
+ d: &InferenceDiagnostic,
+ source_map: &hir_def::body::BodySourceMap,
+ ) -> Option<AnyDiagnostic> {
+ let expr_syntax = |expr| source_map.expr_syntax(expr).expect("unexpected synthetic");
+ let pat_syntax = |pat| source_map.pat_syntax(pat).expect("unexpected synthetic");
+ Some(match d {
+ &InferenceDiagnostic::NoSuchField { field: expr, private } => {
+ let expr_or_pat = match expr {
+ ExprOrPatId::ExprId(expr) => {
+ source_map.field_syntax(expr).map(AstPtr::wrap_left)
+ }
+ ExprOrPatId::PatId(pat) => {
+ source_map.pat_field_syntax(pat).map(AstPtr::wrap_right)
+ }
+ };
+ NoSuchField { field: expr_or_pat, private }.into()
+ }
+ &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
+ MismatchedArgCount { call_expr: expr_syntax(call_expr), expected, found }.into()
+ }
+ &InferenceDiagnostic::PrivateField { expr, field } => {
+ let expr = expr_syntax(expr);
+ let field = field.into();
+ PrivateField { expr, field }.into()
+ }
+ &InferenceDiagnostic::PrivateAssocItem { id, item } => {
+ let expr_or_pat = match id {
+ ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(AstPtr::wrap_left),
+ ExprOrPatId::PatId(pat) => pat_syntax(pat).map(AstPtr::wrap_right),
+ };
+ let item = item.into();
+ PrivateAssocItem { expr_or_pat, item }.into()
+ }
+ InferenceDiagnostic::ExpectedFunction { call_expr, found } => {
+ let call_expr = expr_syntax(*call_expr);
+ ExpectedFunction { call: call_expr, found: Type::new(db, def, found.clone()) }
+ .into()
+ }
+ InferenceDiagnostic::UnresolvedField {
+ expr,
+ receiver,
+ name,
+ method_with_same_name_exists,
+ } => {
+ let expr = expr_syntax(*expr);
+ UnresolvedField {
+ expr,
+ name: name.clone(),
+ receiver: Type::new(db, def, receiver.clone()),
+ method_with_same_name_exists: *method_with_same_name_exists,
+ }
+ .into()
+ }
+ InferenceDiagnostic::UnresolvedMethodCall {
+ expr,
+ receiver,
+ name,
+ field_with_same_name,
+ assoc_func_with_same_name,
+ } => {
+ let expr = expr_syntax(*expr);
+ UnresolvedMethodCall {
+ expr,
+ name: name.clone(),
+ receiver: Type::new(db, def, receiver.clone()),
+ field_with_same_name: field_with_same_name
+ .clone()
+ .map(|ty| Type::new(db, def, ty)),
+ assoc_func_with_same_name: *assoc_func_with_same_name,
+ }
+ .into()
+ }
+ &InferenceDiagnostic::UnresolvedAssocItem { id } => {
+ let expr_or_pat = match id {
+ ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(AstPtr::wrap_left),
+ ExprOrPatId::PatId(pat) => pat_syntax(pat).map(AstPtr::wrap_right),
+ };
+ UnresolvedAssocItem { expr_or_pat }.into()
+ }
+ &InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
+ let expr = expr_syntax(expr);
+ BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()
+ }
+ InferenceDiagnostic::TypedHole { expr, expected } => {
+ let expr = expr_syntax(*expr);
+ TypedHole { expr, expected: Type::new(db, def, expected.clone()) }.into()
+ }
+ &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => {
+ let expr_or_pat = match pat {
+ ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(AstPtr::wrap_left),
+ ExprOrPatId::PatId(pat) => {
+ let InFile { file_id, value } =
+ source_map.pat_syntax(pat).expect("unexpected synthetic");
+
+ // cast from Either<Pat, SelfParam> -> Either<_, Pat>
+ let Some(ptr) = AstPtr::try_from_raw(value.syntax_node_ptr()) else {
+ return None;
+ };
+ InFile { file_id, value: ptr }
+ }
+ };
+ MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into()
+ }
+ })
+ }
+}