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.rs255
1 files changed, 226 insertions, 29 deletions
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 1ed0daa375..b6e3002ed5 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -6,31 +6,33 @@
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_def::{
- expr_store::ExprOrPatPtr,
+ DefWithBodyId, GenericParamId, SyntheticSyntax,
+ expr_store::{
+ ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast,
+ hir_generic_arg_to_ast, hir_segment_to_ast_segment,
+ },
hir::ExprOrPatId,
- path::{hir_segment_to_ast_segment, ModPath},
- type_ref::TypesSourceMap,
- DefWithBodyId, SyntheticSyntax,
};
-use hir_expand::{name::Name, HirFileId, InFile};
+use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name};
use hir_ty::{
+ CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathGenericsSource,
+ PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
db::HirDatabase,
diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
- CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathLoweringDiagnostic,
- TyLoweringDiagnostic, TyLoweringDiagnosticKind,
};
use syntax::{
+ AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange,
ast::{self, HasGenericArgs},
- match_ast, AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange,
+ match_ast,
};
use triomphe::Arc;
-use crate::{AssocItem, Field, Function, Local, Trait, Type};
+use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type};
pub use hir_def::VariantId;
pub use hir_ty::{
+ GenericArgsProhibitedReason, IncorrectGenericsLenKind,
diagnostics::{CaseType, IncorrectCase},
- GenericArgsProhibitedReason,
};
macro_rules! diagnostics {
@@ -113,6 +115,11 @@ diagnostics![
UnusedVariable,
GenericArgsProhibited,
ParenthesizedGenericArgsWithoutFnTrait,
+ BadRtn,
+ IncorrectGenericsLen,
+ IncorrectGenericsOrder,
+ MissingLifetime,
+ ElidedLifetimesInPath,
];
#[derive(Debug)]
@@ -420,6 +427,61 @@ pub struct ParenthesizedGenericArgsWithoutFnTrait {
pub args: InFile<AstPtr<ast::ParenthesizedArgList>>,
}
+#[derive(Debug)]
+pub struct BadRtn {
+ pub rtn: InFile<AstPtr<ast::ReturnTypeSyntax>>,
+}
+
+#[derive(Debug)]
+pub struct IncorrectGenericsLen {
+ /// Points at the name if there are no generics.
+ pub generics_or_segment: InFile<AstPtr<Either<ast::GenericArgList, ast::NameRef>>>,
+ pub kind: IncorrectGenericsLenKind,
+ pub provided: u32,
+ pub expected: u32,
+ pub def: GenericDef,
+}
+
+#[derive(Debug)]
+pub struct MissingLifetime {
+ /// Points at the name if there are no generics.
+ pub generics_or_segment: InFile<AstPtr<Either<ast::GenericArgList, ast::NameRef>>>,
+ pub expected: u32,
+ pub def: GenericDef,
+}
+
+#[derive(Debug)]
+pub struct ElidedLifetimesInPath {
+ /// Points at the name if there are no generics.
+ pub generics_or_segment: InFile<AstPtr<Either<ast::GenericArgList, ast::NameRef>>>,
+ pub expected: u32,
+ pub def: GenericDef,
+ pub hard_error: bool,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum GenericArgKind {
+ Lifetime,
+ Type,
+ Const,
+}
+
+impl GenericArgKind {
+ fn from_id(id: GenericParamId) -> Self {
+ match id {
+ GenericParamId::TypeParamId(_) => GenericArgKind::Type,
+ GenericParamId::ConstParamId(_) => GenericArgKind::Const,
+ GenericParamId::LifetimeParamId(_) => GenericArgKind::Lifetime,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct IncorrectGenericsOrder {
+ pub provided_arg: InFile<AstPtr<ast::GenericArg>>,
+ pub expected_kind: GenericArgKind,
+}
+
impl AnyDiagnostic {
pub(crate) fn body_validation_diagnostic(
db: &dyn HirDatabase,
@@ -428,7 +490,7 @@ impl AnyDiagnostic {
) -> Option<AnyDiagnostic> {
match diagnostic {
BodyValidationDiagnostic::RecordMissingFields { record, variant, missed_fields } => {
- let variant_data = variant.variant_data(db.upcast());
+ let variant_data = variant.variant_data(db);
let missed_fields = missed_fields
.into_iter()
.map(|idx| variant_data.fields()[idx].name.clone())
@@ -439,7 +501,7 @@ impl AnyDiagnostic {
Either::Right(record_pat) => source_map.pat_syntax(record_pat).ok()?,
};
let file = record.file_id;
- let root = record.file_syntax(db.upcast());
+ let root = record.file_syntax(db);
match record.value.to_node(&root) {
Either::Left(ast::Expr::RecordExpr(record_expr)) => {
if record_expr.record_expr_field_list().is_some() {
@@ -488,7 +550,7 @@ impl AnyDiagnostic {
BodyValidationDiagnostic::MissingMatchArms { match_expr, uncovered_patterns } => {
match source_map.expr_syntax(match_expr) {
Ok(source_ptr) => {
- let root = source_ptr.file_syntax(db.upcast());
+ let root = source_ptr.file_syntax(db);
if let Either::Left(ast::Expr::MatchExpr(match_expr)) =
&source_ptr.value.to_node(&root)
{
@@ -559,14 +621,21 @@ impl AnyDiagnostic {
db: &dyn HirDatabase,
def: DefWithBodyId,
d: &InferenceDiagnostic,
- outer_types_source_map: &TypesSourceMap,
source_map: &hir_def::expr_store::BodySourceMap,
+ sig_map: &hir_def::expr_store::ExpressionStoreSourceMap,
) -> Option<AnyDiagnostic> {
let expr_syntax = |expr| {
- source_map.expr_syntax(expr).inspect_err(|_| stdx::never!("synthetic syntax")).ok()
+ source_map
+ .expr_syntax(expr)
+ .inspect_err(|_| stdx::never!("inference diagnostic in desugared expr"))
+ .ok()
+ };
+ let pat_syntax = |pat| {
+ source_map
+ .pat_syntax(pat)
+ .inspect_err(|_| stdx::never!("inference diagnostic in desugared pattern"))
+ .ok()
};
- let pat_syntax =
- |pat| source_map.pat_syntax(pat).inspect_err(|_| stdx::never!("synthetic syntax")).ok();
let expr_or_pat_syntax = |id| match id {
ExprOrPatId::ExprId(expr) => expr_syntax(expr),
ExprOrPatId::PatId(pat) => pat_syntax(pat),
@@ -682,8 +751,8 @@ impl AnyDiagnostic {
}
InferenceDiagnostic::TyDiagnostic { source, diag } => {
let source_map = match source {
- InferenceTyDiagnosticSource::Body => &source_map.types,
- InferenceTyDiagnosticSource::Signature => outer_types_source_map,
+ InferenceTyDiagnosticSource::Body => source_map,
+ InferenceTyDiagnosticSource::Signature => sig_map,
};
Self::ty_diagnostic(diag, source_map, db)?
}
@@ -702,6 +771,47 @@ impl AnyDiagnostic {
};
Self::path_diagnostic(diag, source.with_value(path))?
}
+ &InferenceDiagnostic::MethodCallIncorrectGenericsLen {
+ expr,
+ provided_count,
+ expected_count,
+ kind,
+ def,
+ } => {
+ let syntax = expr_syntax(expr)?;
+ let file_id = syntax.file_id;
+ let syntax =
+ syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
+ let generics_or_name = syntax
+ .generic_arg_list()
+ .map(Either::Left)
+ .or_else(|| syntax.name_ref().map(Either::Right))?;
+ let generics_or_name = InFile::new(file_id, AstPtr::new(&generics_or_name));
+ IncorrectGenericsLen {
+ generics_or_segment: generics_or_name,
+ kind,
+ provided: provided_count,
+ expected: expected_count,
+ def: def.into(),
+ }
+ .into()
+ }
+ &InferenceDiagnostic::MethodCallIncorrectGenericsOrder {
+ expr,
+ param_id,
+ arg_idx,
+ has_self_arg,
+ } => {
+ let syntax = expr_syntax(expr)?;
+ let file_id = syntax.file_id;
+ let syntax =
+ syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
+ let generic_args = syntax.generic_arg_list()?;
+ let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
+ let provided_arg = InFile::new(file_id, AstPtr::new(&provided_arg));
+ let expected_kind = GenericArgKind::from_id(param_id);
+ IncorrectGenericsOrder { provided_arg, expected_kind }.into()
+ }
})
}
@@ -712,6 +822,12 @@ impl AnyDiagnostic {
Some(match *diag {
PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => {
let segment = hir_segment_to_ast_segment(&path.value, segment)?;
+
+ if let Some(rtn) = segment.return_type_syntax() {
+ // RTN errors are emitted as `GenericArgsProhibited` or `ParenthesizedGenericArgsWithoutFnTrait`.
+ return Some(BadRtn { rtn: path.with_value(AstPtr::new(&rtn)) }.into());
+ }
+
let args = if let Some(generics) = segment.generic_arg_list() {
AstPtr::new(&generics).wrap_left()
} else {
@@ -722,27 +838,84 @@ impl AnyDiagnostic {
}
PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment } => {
let segment = hir_segment_to_ast_segment(&path.value, segment)?;
+
+ if let Some(rtn) = segment.return_type_syntax() {
+ // RTN errors are emitted as `GenericArgsProhibited` or `ParenthesizedGenericArgsWithoutFnTrait`.
+ return Some(BadRtn { rtn: path.with_value(AstPtr::new(&rtn)) }.into());
+ }
+
let args = AstPtr::new(&segment.parenthesized_arg_list()?);
let args = path.with_value(args);
ParenthesizedGenericArgsWithoutFnTrait { args }.into()
}
+ PathLoweringDiagnostic::IncorrectGenericsLen {
+ generics_source,
+ provided_count,
+ expected_count,
+ kind,
+ def,
+ } => {
+ let generics_or_segment =
+ path_generics_source_to_ast(&path.value, generics_source)?;
+ let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
+ IncorrectGenericsLen {
+ generics_or_segment,
+ kind,
+ provided: provided_count,
+ expected: expected_count,
+ def: def.into(),
+ }
+ .into()
+ }
+ PathLoweringDiagnostic::IncorrectGenericsOrder {
+ generics_source,
+ param_id,
+ arg_idx,
+ has_self_arg,
+ } => {
+ let generic_args =
+ path_generics_source_to_ast(&path.value, generics_source)?.left()?;
+ let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
+ let provided_arg = path.with_value(AstPtr::new(&provided_arg));
+ let expected_kind = GenericArgKind::from_id(param_id);
+ IncorrectGenericsOrder { provided_arg, expected_kind }.into()
+ }
+ PathLoweringDiagnostic::MissingLifetime { generics_source, expected_count, def }
+ | PathLoweringDiagnostic::ElisionFailure { generics_source, expected_count, def } => {
+ let generics_or_segment =
+ path_generics_source_to_ast(&path.value, generics_source)?;
+ let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
+ MissingLifetime { generics_or_segment, expected: expected_count, def: def.into() }
+ .into()
+ }
+ PathLoweringDiagnostic::ElidedLifetimesInPath {
+ generics_source,
+ expected_count,
+ def,
+ hard_error,
+ } => {
+ let generics_or_segment =
+ path_generics_source_to_ast(&path.value, generics_source)?;
+ let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
+ ElidedLifetimesInPath {
+ generics_or_segment,
+ expected: expected_count,
+ def: def.into(),
+ hard_error,
+ }
+ .into()
+ }
})
}
pub(crate) fn ty_diagnostic(
diag: &TyLoweringDiagnostic,
- source_map: &TypesSourceMap,
+ source_map: &ExpressionStoreSourceMap,
db: &dyn HirDatabase,
) -> Option<AnyDiagnostic> {
- let source = match diag.source {
- Either::Left(type_ref_id) => {
- let Ok(source) = source_map.type_syntax(type_ref_id) else {
- stdx::never!("error on synthetic type syntax");
- return None;
- };
- source
- }
- Either::Right(source) => source,
+ let Ok(source) = source_map.type_syntax(diag.source) else {
+ stdx::never!("error on synthetic type syntax");
+ return None;
};
let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id));
Some(match &diag.kind {
@@ -753,3 +926,27 @@ impl AnyDiagnostic {
})
}
}
+
+fn path_generics_source_to_ast(
+ path: &ast::Path,
+ generics_source: PathGenericsSource,
+) -> Option<Either<ast::GenericArgList, ast::NameRef>> {
+ Some(match generics_source {
+ PathGenericsSource::Segment(segment) => {
+ let segment = hir_segment_to_ast_segment(path, segment)?;
+ segment
+ .generic_arg_list()
+ .map(Either::Left)
+ .or_else(|| segment.name_ref().map(Either::Right))?
+ }
+ PathGenericsSource::AssocType { segment, assoc_type } => {
+ let segment = hir_segment_to_ast_segment(path, segment)?;
+ let segment_args = segment.generic_arg_list()?;
+ let assoc = hir_assoc_type_binding_to_ast(&segment_args, assoc_type)?;
+ assoc
+ .generic_arg_list()
+ .map(Either::Left)
+ .or_else(|| assoc.name_ref().map(Either::Right))?
+ }
+ })
+}