Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22469 from Vlm326/diagnostics/no-infer-vars
feat(diagnostics): emit error for infer vars in non-inference contexts
Chayim Refael Friedman 14 days ago
parent 4af3f44 · parent 725cd24 · commit a0a5c97
-rw-r--r--crates/hir-ty/src/lower.rs23
-rw-r--r--crates/hir-ty/src/lower/diagnostics.rs14
-rw-r--r--crates/hir/src/diagnostics.rs112
-rw-r--r--crates/ide-diagnostics/src/handlers/infer_vars_not_allowed.rs49
-rw-r--r--crates/ide-diagnostics/src/lib.rs2
5 files changed, 144 insertions, 56 deletions
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index df83b2abb8..985a3ddc6a 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -55,7 +55,7 @@ use thin_vec::ThinVec;
use tracing::debug;
use crate::{
- ImplTraitId, Span, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
+ ImplTraitId, Span, TyLoweringDiagnostic,
consteval::{create_anon_const, path_to_const},
db::{AnonConstId, GeneralConstId, HirDatabase, InternedOpaqueTyId},
generics::{Generics, SingleGenerics, generics},
@@ -302,8 +302,14 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
self
}
- pub(crate) fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) {
- self.diagnostics.push(TyLoweringDiagnostic { source: type_ref, kind });
+ pub(crate) fn push_diagnostic(&mut self, diagnostic: TyLoweringDiagnostic) {
+ self.diagnostics.push(diagnostic);
+ }
+
+ fn push_infer_vars_not_allowed(&mut self, span: Span) {
+ if !span.is_dummy() {
+ self.push_diagnostic(TyLoweringDiagnostic::InferVarsNotAllowed { source: span });
+ }
}
#[track_caller]
@@ -315,7 +321,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
match &mut self.infer_vars {
Some(infer_vars) => infer_vars.next_ty_var(span),
None => {
- // FIXME: Emit an error: no infer vars allowed here.
+ self.push_infer_vars_not_allowed(span);
self.types.types.error
}
}
@@ -325,7 +331,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
match &mut self.infer_vars {
Some(infer_vars) => infer_vars.next_const_var(span),
None => {
- // FIXME: Emit an error: no infer vars allowed here.
+ self.push_infer_vars_not_allowed(span);
self.types.consts.error
}
}
@@ -335,7 +341,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
match &mut self.infer_vars {
Some(infer_vars) => infer_vars.next_region_var(span),
None => {
- // FIXME: Emit an error: no infer vars allowed here.
+ self.push_infer_vars_not_allowed(span);
self.types.regions.error
}
}
@@ -634,7 +640,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
data: Either::Left(PathDiagnosticCallbackData(type_ref)),
callback: |data, this, diag| {
let type_ref = data.as_ref().left().unwrap().0;
- this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag))
+ this.push_diagnostic(TyLoweringDiagnostic::PathDiagnostic {
+ source: type_ref,
+ diag,
+ })
},
}
}
diff --git a/crates/hir-ty/src/lower/diagnostics.rs b/crates/hir-ty/src/lower/diagnostics.rs
index 2565fb46ce..35ee99cb95 100644
--- a/crates/hir-ty/src/lower/diagnostics.rs
+++ b/crates/hir-ty/src/lower/diagnostics.rs
@@ -1,17 +1,13 @@
//! This files contains the declaration of diagnostics kinds for ty and path lowering.
-use hir_def::type_ref::TypeRefId;
-use hir_def::{GenericDefId, GenericParamId};
+use hir_def::{GenericDefId, GenericParamId, type_ref::TypeRefId};
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct TyLoweringDiagnostic {
- pub source: TypeRefId,
- pub kind: TyLoweringDiagnosticKind,
-}
+use crate::Span;
#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum TyLoweringDiagnosticKind {
- PathDiagnostic(PathLoweringDiagnostic),
+pub enum TyLoweringDiagnostic {
+ PathDiagnostic { source: TypeRefId, diag: PathLoweringDiagnostic },
+ InferVarsNotAllowed { source: Span },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index f3188c9aad..ed073cb437 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -11,12 +11,13 @@ use hir_def::{
ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast,
hir_generic_arg_to_ast, hir_segment_to_ast_segment,
},
- hir::ExprOrPatId,
+ hir::{ExprId, ExprOrPatId, PatId},
+ type_ref::TypeRefId,
};
use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name};
use hir_ty::{
CastError, ExplicitDropMethodUseKind, InferenceDiagnostic, InferenceTyDiagnosticSource,
- PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
+ PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic,
db::HirDatabase,
diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
display::{DisplayTarget, HirDisplay},
@@ -118,6 +119,7 @@ diagnostics![AnyDiagnostic<'db> ->
IncorrectCase,
IncorrectGenericsLen,
IncorrectGenericsOrder,
+ InferVarsNotAllowed,
InvalidCast<'db>,
InvalidDeriveTarget,
InvalidLhsOfAssignment,
@@ -565,6 +567,11 @@ pub struct BadRtn {
}
#[derive(Debug)]
+pub struct InferVarsNotAllowed {
+ pub node: InFile<SyntaxNodePtr>,
+}
+
+#[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>>>,
@@ -799,41 +806,14 @@ impl<'db> AnyDiagnostic<'db> {
sig_map: &hir_def::expr_store::ExpressionStoreSourceMap,
type_owner: TypeOwnerId,
) -> Option<AnyDiagnostic<'db>> {
- let expr_syntax = |expr| {
- 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 type_syntax = |pat| {
- source_map
- .type_syntax(pat)
- .inspect_err(|_| stdx::never!("inference diagnostic in desugared type"))
- .ok()
- };
+ let expr_syntax = |expr| Self::expr_syntax(expr, source_map);
+ let pat_syntax = |pat| Self::pat_syntax(pat, source_map);
let expr_or_pat_syntax = |id| match id {
ExprOrPatId::ExprId(expr) => expr_syntax(expr),
ExprOrPatId::PatId(pat) => pat_syntax(pat),
};
let new_ty = |ty| Type { owner: type_owner, ty: EarlyBinder::bind(ty) };
- let span_syntax = |span| match span {
- hir_ty::Span::ExprId(idx) => expr_syntax(idx).map(|it| it.upcast()),
- hir_ty::Span::PatId(idx) => pat_syntax(idx).map(|it| it.upcast()),
- hir_ty::Span::TypeRefId(idx) => type_syntax(idx).map(|it| it.upcast()),
- hir_ty::Span::BindingId(idx) => {
- pat_syntax(source_map.patterns_for_binding(idx)[0]).map(|it| it.upcast())
- }
- hir_ty::Span::Dummy => {
- never!("should never create a diagnostic for dummy spans");
- None
- }
- };
+ let span_syntax = |span| Self::span_syntax(span, source_map);
Some(match d {
&InferenceDiagnostic::NoSuchField { field: expr, private, variant } => {
let expr_or_pat = match expr {
@@ -1238,21 +1218,73 @@ impl<'db> AnyDiagnostic<'db> {
})
}
+ fn expr_syntax(
+ expr: ExprId,
+ source_map: &ExpressionStoreSourceMap,
+ ) -> Option<InFile<ExprOrPatPtr>> {
+ source_map
+ .expr_syntax(expr)
+ .inspect_err(|_| stdx::never!("inference diagnostic in desugared expr"))
+ .ok()
+ }
+
+ fn pat_syntax(
+ pat: PatId,
+ source_map: &ExpressionStoreSourceMap,
+ ) -> Option<InFile<ExprOrPatPtr>> {
+ source_map
+ .pat_syntax(pat)
+ .inspect_err(|_| stdx::never!("inference diagnostic in desugared pattern"))
+ .ok()
+ }
+
+ fn type_syntax(
+ type_ref: TypeRefId,
+ source_map: &ExpressionStoreSourceMap,
+ ) -> Option<InFile<AstPtr<ast::Type>>> {
+ source_map
+ .type_syntax(type_ref)
+ .inspect_err(|_| stdx::never!("inference diagnostic in desugared type"))
+ .ok()
+ }
+
+ fn span_syntax(
+ span: hir_ty::Span,
+ source_map: &ExpressionStoreSourceMap,
+ ) -> Option<InFile<AstPtr<SpanAst>>> {
+ Some(match span {
+ hir_ty::Span::ExprId(idx) => Self::expr_syntax(idx, source_map)?.map(|it| it.upcast()),
+ hir_ty::Span::PatId(idx) => Self::pat_syntax(idx, source_map)?.map(|it| it.upcast()),
+ hir_ty::Span::TypeRefId(idx) => {
+ Self::type_syntax(idx, source_map)?.map(|it| it.upcast())
+ }
+ hir_ty::Span::BindingId(idx) => {
+ let &pat = source_map.patterns_for_binding(idx).first()?;
+ Self::pat_syntax(pat, source_map)?.map(|it| it.upcast())
+ }
+ hir_ty::Span::Dummy => {
+ never!("should never create a diagnostic for dummy spans");
+ return None;
+ }
+ })
+ }
+
pub(crate) fn ty_diagnostic(
diag: &TyLoweringDiagnostic,
source_map: &ExpressionStoreSourceMap,
db: &'db dyn HirDatabase,
) -> Option<AnyDiagnostic<'db>> {
- 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 {
- TyLoweringDiagnosticKind::PathDiagnostic(diag) => {
- let ast::Type::PathType(syntax) = syntax() else { return None };
+ Some(match diag {
+ TyLoweringDiagnostic::PathDiagnostic { source, diag } => {
+ let source = Self::type_syntax(*source, source_map)?;
+ let syntax = source.value.to_node(&db.parse_or_expand(source.file_id));
+ let ast::Type::PathType(syntax) = syntax else { return None };
Self::path_diagnostic(diag, source.with_value(syntax.path()?))?
}
+ TyLoweringDiagnostic::InferVarsNotAllowed { source } => {
+ let source = Self::span_syntax(*source, source_map)?;
+ InferVarsNotAllowed { node: source.map(Into::into) }.into()
+ }
})
}
}
diff --git a/crates/ide-diagnostics/src/handlers/infer_vars_not_allowed.rs b/crates/ide-diagnostics/src/handlers/infer_vars_not_allowed.rs
new file mode 100644
index 0000000000..cf369a1aa0
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/infer_vars_not_allowed.rs
@@ -0,0 +1,49 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: infer-vars-not-allowed
+//
+// This diagnostic is triggered when `_` is used where type
+// inference is not allowed.
+pub(crate) fn infer_vars_not_allowed(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::InferVarsNotAllowed,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0121"),
+ "the type placeholder `_` is not allowed within types on item signatures",
+ d.node,
+ )
+}
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+ #[test]
+ fn type_alias() {
+ check_diagnostics(
+ r#"
+type Foo = _;
+ // ^ error: the type placeholder `_` is not allowed within types on item signatures
+ "#,
+ );
+ }
+ #[test]
+ fn const_item() {
+ check_diagnostics(
+ r#"
+const X: _ = 0;
+ // ^ error: the type placeholder `_` is not allowed within types on item signatures
+ "#,
+ );
+ }
+
+ #[test]
+ fn static_item() {
+ check_diagnostics(
+ r#"
+static Y: _ = 0;
+ // ^ error: the type placeholder `_` is not allowed within types on item signatures
+ "#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index aec68b55c7..ea0d2d954e 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -50,6 +50,7 @@ mod handlers {
pub(crate) mod incorrect_case;
pub(crate) mod incorrect_generics_len;
pub(crate) mod incorrect_generics_order;
+ pub(crate) mod infer_vars_not_allowed;
pub(crate) mod invalid_cast;
pub(crate) mod invalid_derive_target;
pub(crate) mod invalid_lhs_of_assignment;
@@ -440,6 +441,7 @@ pub fn semantic_diagnostics(
AnyDiagnostic::CannotImplicitlyDerefTraitObject(d) => handlers::cannot_implicitly_deref_trait_object::cannot_implicitly_deref_trait_object(&ctx, &d),
AnyDiagnostic::CannotIndexInto(d) => handlers::cannot_index_into::cannot_index_into(&ctx, &d),
AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d),
+ AnyDiagnostic::InferVarsNotAllowed(d) => handlers::infer_vars_not_allowed::infer_vars_not_allowed(&ctx, &d),
AnyDiagnostic::ArrayPatternWithoutFixedLength(d) => {
handlers::array_pattern_without_fixed_length::array_pattern_without_fixed_length(
&ctx, &d,