mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-28 01:34:21 +00:00
internal: move inference diagnostics to hir
This commit is contained in:
parent
409f5fb563
commit
f8009666be
@ -14,8 +14,7 @@ use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
|
|||||||
pub use hir_ty::{
|
pub use hir_ty::{
|
||||||
diagnostics::{
|
diagnostics::{
|
||||||
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms,
|
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms,
|
||||||
MissingOkOrSomeInTailExpr, NoSuchField, RemoveThisSemicolon,
|
MissingOkOrSomeInTailExpr, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
|
||||||
ReplaceFilterMapNextWithFindMap,
|
|
||||||
},
|
},
|
||||||
diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder},
|
diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder},
|
||||||
};
|
};
|
||||||
@ -251,3 +250,54 @@ impl Diagnostic for UnimplementedBuiltinMacro {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Diagnostic: no-such-field
|
||||||
|
//
|
||||||
|
// This diagnostic is triggered if created structure does not have field provided in record.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NoSuchField {
|
||||||
|
pub file: HirFileId,
|
||||||
|
pub field: AstPtr<ast::RecordExprField>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic for NoSuchField {
|
||||||
|
fn code(&self) -> DiagnosticCode {
|
||||||
|
DiagnosticCode("no-such-field")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn message(&self) -> String {
|
||||||
|
"no such field".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
||||||
|
InFile::new(self.file, self.field.clone().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diagnostic: break-outside-of-loop
|
||||||
|
//
|
||||||
|
// This diagnostic is triggered if the `break` keyword is used outside of a loop.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BreakOutsideOfLoop {
|
||||||
|
pub file: HirFileId,
|
||||||
|
pub expr: AstPtr<ast::Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic for BreakOutsideOfLoop {
|
||||||
|
fn code(&self) -> DiagnosticCode {
|
||||||
|
DiagnosticCode("break-outside-of-loop")
|
||||||
|
}
|
||||||
|
fn message(&self) -> String {
|
||||||
|
"break outside of loop".to_string()
|
||||||
|
}
|
||||||
|
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
||||||
|
InFile { file_id: self.file, value: self.expr.clone().into() }
|
||||||
|
}
|
||||||
|
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -36,8 +36,9 @@ use std::{iter, sync::Arc};
|
|||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use base_db::{CrateDisplayName, CrateId, Edition, FileId};
|
use base_db::{CrateDisplayName, CrateId, Edition, FileId};
|
||||||
use diagnostics::{
|
use diagnostics::{
|
||||||
InactiveCode, MacroError, UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport,
|
BreakOutsideOfLoop, InactiveCode, MacroError, NoSuchField, UnimplementedBuiltinMacro,
|
||||||
UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
|
UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
|
||||||
|
UnresolvedProcMacro,
|
||||||
};
|
};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
@ -1042,6 +1043,23 @@ impl Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let infer = db.infer(self.id.into());
|
||||||
|
let (_, source_map) = db.body_with_source_map(self.id.into());
|
||||||
|
for d in &infer.diagnostics {
|
||||||
|
match d {
|
||||||
|
hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
|
||||||
|
let field = source_map.field_syntax(*expr);
|
||||||
|
sink.push(NoSuchField { file: field.file_id, field: field.value })
|
||||||
|
}
|
||||||
|
hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
|
||||||
|
let ptr = source_map
|
||||||
|
.expr_syntax(*expr)
|
||||||
|
.expect("break outside of loop in synthetic syntax");
|
||||||
|
sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink);
|
hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink);
|
||||||
hir_ty::diagnostics::validate_body(db, self.id.into(), sink);
|
hir_ty::diagnostics::validate_body(db, self.id.into(), sink);
|
||||||
}
|
}
|
||||||
|
@ -33,40 +33,12 @@ pub fn validate_module_item(
|
|||||||
pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
|
pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
|
||||||
let _p = profile::span("validate_body");
|
let _p = profile::span("validate_body");
|
||||||
let infer = db.infer(owner);
|
let infer = db.infer(owner);
|
||||||
infer.add_diagnostics(db, owner, sink);
|
|
||||||
let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink);
|
let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink);
|
||||||
validator.validate_body(db);
|
validator.validate_body(db);
|
||||||
let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink);
|
let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink);
|
||||||
validator.validate_body(db);
|
validator.validate_body(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diagnostic: no-such-field
|
|
||||||
//
|
|
||||||
// This diagnostic is triggered if created structure does not have field provided in record.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct NoSuchField {
|
|
||||||
pub file: HirFileId,
|
|
||||||
pub field: AstPtr<ast::RecordExprField>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Diagnostic for NoSuchField {
|
|
||||||
fn code(&self) -> DiagnosticCode {
|
|
||||||
DiagnosticCode("no-such-field")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn message(&self) -> String {
|
|
||||||
"no such field".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
|
||||||
InFile::new(self.file, self.field.clone().into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Diagnostic: missing-structure-fields
|
// Diagnostic: missing-structure-fields
|
||||||
//
|
//
|
||||||
// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
|
// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
|
||||||
@ -247,30 +219,6 @@ impl Diagnostic for RemoveThisSemicolon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diagnostic: break-outside-of-loop
|
|
||||||
//
|
|
||||||
// This diagnostic is triggered if the `break` keyword is used outside of a loop.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct BreakOutsideOfLoop {
|
|
||||||
pub file: HirFileId,
|
|
||||||
pub expr: AstPtr<ast::Expr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Diagnostic for BreakOutsideOfLoop {
|
|
||||||
fn code(&self) -> DiagnosticCode {
|
|
||||||
DiagnosticCode("break-outside-of-loop")
|
|
||||||
}
|
|
||||||
fn message(&self) -> String {
|
|
||||||
"break outside of loop".to_string()
|
|
||||||
}
|
|
||||||
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
|
||||||
InFile { file_id: self.file, value: self.expr.clone().into() }
|
|
||||||
}
|
|
||||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Diagnostic: missing-unsafe
|
// Diagnostic: missing-unsafe
|
||||||
//
|
//
|
||||||
// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
|
// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
|
||||||
@ -530,129 +478,6 @@ mod tests {
|
|||||||
assert_eq!(annotations, actual);
|
assert_eq!(annotations, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn no_such_field_diagnostics() {
|
|
||||||
check_diagnostics(
|
|
||||||
r#"
|
|
||||||
struct S { foo: i32, bar: () }
|
|
||||||
impl S {
|
|
||||||
fn new() -> S {
|
|
||||||
S {
|
|
||||||
//^ Missing structure fields:
|
|
||||||
//| - bar
|
|
||||||
foo: 92,
|
|
||||||
baz: 62,
|
|
||||||
//^^^^^^^ no such field
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn no_such_field_with_feature_flag_diagnostics() {
|
|
||||||
check_diagnostics(
|
|
||||||
r#"
|
|
||||||
//- /lib.rs crate:foo cfg:feature=foo
|
|
||||||
struct MyStruct {
|
|
||||||
my_val: usize,
|
|
||||||
#[cfg(feature = "foo")]
|
|
||||||
bar: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MyStruct {
|
|
||||||
#[cfg(feature = "foo")]
|
|
||||||
pub(crate) fn new(my_val: usize, bar: bool) -> Self {
|
|
||||||
Self { my_val, bar }
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "foo"))]
|
|
||||||
pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
|
|
||||||
Self { my_val }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn no_such_field_enum_with_feature_flag_diagnostics() {
|
|
||||||
check_diagnostics(
|
|
||||||
r#"
|
|
||||||
//- /lib.rs crate:foo cfg:feature=foo
|
|
||||||
enum Foo {
|
|
||||||
#[cfg(not(feature = "foo"))]
|
|
||||||
Buz,
|
|
||||||
#[cfg(feature = "foo")]
|
|
||||||
Bar,
|
|
||||||
Baz
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_fn(f: Foo) {
|
|
||||||
match f {
|
|
||||||
Foo::Bar => {},
|
|
||||||
Foo::Baz => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
|
|
||||||
check_diagnostics(
|
|
||||||
r#"
|
|
||||||
//- /lib.rs crate:foo cfg:feature=foo
|
|
||||||
struct S {
|
|
||||||
#[cfg(feature = "foo")]
|
|
||||||
foo: u32,
|
|
||||||
#[cfg(not(feature = "foo"))]
|
|
||||||
bar: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl S {
|
|
||||||
#[cfg(feature = "foo")]
|
|
||||||
fn new(foo: u32) -> Self {
|
|
||||||
Self { foo }
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "foo"))]
|
|
||||||
fn new(bar: u32) -> Self {
|
|
||||||
Self { bar }
|
|
||||||
}
|
|
||||||
fn new2(bar: u32) -> Self {
|
|
||||||
#[cfg(feature = "foo")]
|
|
||||||
{ Self { foo: bar } }
|
|
||||||
#[cfg(not(feature = "foo"))]
|
|
||||||
{ Self { bar } }
|
|
||||||
}
|
|
||||||
fn new2(val: u32) -> Self {
|
|
||||||
Self {
|
|
||||||
#[cfg(feature = "foo")]
|
|
||||||
foo: val,
|
|
||||||
#[cfg(not(feature = "foo"))]
|
|
||||||
bar: val,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn no_such_field_with_type_macro() {
|
|
||||||
check_diagnostics(
|
|
||||||
r#"
|
|
||||||
macro_rules! Type { () => { u32 }; }
|
|
||||||
struct Foo { bar: Type![] }
|
|
||||||
|
|
||||||
impl Foo {
|
|
||||||
fn new() -> Self {
|
|
||||||
Foo { bar: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_record_pat_field_diagnostic() {
|
fn missing_record_pat_field_diagnostic() {
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
@ -734,16 +559,6 @@ pub struct Claims {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn break_outside_of_loop() {
|
|
||||||
check_diagnostics(
|
|
||||||
r#"
|
|
||||||
fn foo() { break; }
|
|
||||||
//^^^^^ break outside of loop
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_semicolon() {
|
fn missing_semicolon() {
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
|
@ -35,11 +35,9 @@ use stdx::impl_from;
|
|||||||
use syntax::SmolStr;
|
use syntax::SmolStr;
|
||||||
|
|
||||||
use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
|
use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
|
||||||
use crate::diagnostics_sink::DiagnosticSink;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic,
|
db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode,
|
||||||
lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution,
|
to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, TyBuilder, TyExt, TyKind,
|
||||||
TyBuilder, TyExt, TyKind,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// This lint has a false positive here. See the link below for details.
|
// This lint has a false positive here. See the link below for details.
|
||||||
@ -111,6 +109,12 @@ pub(crate) struct InferOk {
|
|||||||
pub(crate) struct TypeError;
|
pub(crate) struct TypeError;
|
||||||
pub(crate) type InferResult = Result<InferOk, TypeError>;
|
pub(crate) type InferResult = Result<InferOk, TypeError>;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub enum InferenceDiagnostic {
|
||||||
|
NoSuchField { expr: ExprId },
|
||||||
|
BreakOutsideOfLoop { expr: ExprId },
|
||||||
|
}
|
||||||
|
|
||||||
/// A mismatch between an expected and an inferred type.
|
/// A mismatch between an expected and an inferred type.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||||
pub struct TypeMismatch {
|
pub struct TypeMismatch {
|
||||||
@ -140,7 +144,7 @@ pub struct InferenceResult {
|
|||||||
variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
|
variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
|
||||||
/// For each associated item record what it resolves to
|
/// For each associated item record what it resolves to
|
||||||
assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>,
|
assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>,
|
||||||
diagnostics: Vec<InferenceDiagnostic>,
|
pub diagnostics: Vec<InferenceDiagnostic>,
|
||||||
pub type_of_expr: ArenaMap<ExprId, Ty>,
|
pub type_of_expr: ArenaMap<ExprId, Ty>,
|
||||||
/// For each pattern record the type it resolves to.
|
/// For each pattern record the type it resolves to.
|
||||||
///
|
///
|
||||||
@ -191,14 +195,6 @@ impl InferenceResult {
|
|||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn add_diagnostics(
|
|
||||||
&self,
|
|
||||||
db: &dyn HirDatabase,
|
|
||||||
owner: DefWithBodyId,
|
|
||||||
sink: &mut DiagnosticSink,
|
|
||||||
) {
|
|
||||||
self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<ExprId> for InferenceResult {
|
impl Index<ExprId> for InferenceResult {
|
||||||
@ -804,43 +800,3 @@ impl std::ops::BitOrAssign for Diverges {
|
|||||||
*self = *self | other;
|
*self = *self | other;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod diagnostics {
|
|
||||||
use hir_def::{expr::ExprId, DefWithBodyId};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
db::HirDatabase,
|
|
||||||
diagnostics::{BreakOutsideOfLoop, NoSuchField},
|
|
||||||
diagnostics_sink::DiagnosticSink,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
||||||
pub(super) enum InferenceDiagnostic {
|
|
||||||
NoSuchField { expr: ExprId },
|
|
||||||
BreakOutsideOfLoop { expr: ExprId },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InferenceDiagnostic {
|
|
||||||
pub(super) fn add_to(
|
|
||||||
&self,
|
|
||||||
db: &dyn HirDatabase,
|
|
||||||
owner: DefWithBodyId,
|
|
||||||
sink: &mut DiagnosticSink,
|
|
||||||
) {
|
|
||||||
match self {
|
|
||||||
InferenceDiagnostic::NoSuchField { expr } => {
|
|
||||||
let (_, source_map) = db.body_with_source_map(owner);
|
|
||||||
let field = source_map.field_syntax(*expr);
|
|
||||||
sink.push(NoSuchField { file: field.file_id, field: field.value })
|
|
||||||
}
|
|
||||||
InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
|
|
||||||
let (_, source_map) = db.body_with_source_map(owner);
|
|
||||||
let ptr = source_map
|
|
||||||
.expr_syntax(*expr)
|
|
||||||
.expect("break outside of loop in synthetic syntax");
|
|
||||||
sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -50,7 +50,7 @@ use crate::{db::HirDatabase, utils::generics};
|
|||||||
pub use autoderef::autoderef;
|
pub use autoderef::autoderef;
|
||||||
pub use builder::TyBuilder;
|
pub use builder::TyBuilder;
|
||||||
pub use chalk_ext::*;
|
pub use chalk_ext::*;
|
||||||
pub use infer::{could_unify, InferenceResult};
|
pub use infer::{could_unify, InferenceDiagnostic, InferenceResult};
|
||||||
pub use interner::Interner;
|
pub use interner::Interner;
|
||||||
pub use lower::{
|
pub use lower::{
|
||||||
associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode,
|
associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode,
|
||||||
|
@ -305,6 +305,7 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use expect_test::Expect;
|
use expect_test::Expect;
|
||||||
|
use hir::diagnostics::DiagnosticCode;
|
||||||
use ide_assists::AssistResolveStrategy;
|
use ide_assists::AssistResolveStrategy;
|
||||||
use stdx::trim_indent;
|
use stdx::trim_indent;
|
||||||
use test_utils::{assert_eq_text, extract_annotations};
|
use test_utils::{assert_eq_text, extract_annotations};
|
||||||
@ -410,7 +411,12 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
|
let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
|
||||||
let actual = diagnostics.into_iter().map(|d| (d.range, d.message)).collect::<Vec<_>>();
|
let mut actual = diagnostics
|
||||||
|
.into_iter()
|
||||||
|
.filter(|d| d.code != Some(DiagnosticCode("inactive-code")))
|
||||||
|
.map(|d| (d.range, d.message))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
actual.sort_by_key(|(range, _)| range.start());
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -716,6 +722,139 @@ $0
|
|||||||
mod foo;
|
mod foo;
|
||||||
|
|
||||||
//- /foo.rs
|
//- /foo.rs
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn break_outside_of_loop() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
fn foo() { break; }
|
||||||
|
//^^^^^ break outside of loop
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_such_field_diagnostics() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
struct S { foo: i32, bar: () }
|
||||||
|
impl S {
|
||||||
|
fn new() -> S {
|
||||||
|
S {
|
||||||
|
//^ Missing structure fields:
|
||||||
|
//| - bar
|
||||||
|
foo: 92,
|
||||||
|
baz: 62,
|
||||||
|
//^^^^^^^ no such field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn no_such_field_with_feature_flag_diagnostics() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- /lib.rs crate:foo cfg:feature=foo
|
||||||
|
struct MyStruct {
|
||||||
|
my_val: usize,
|
||||||
|
#[cfg(feature = "foo")]
|
||||||
|
bar: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MyStruct {
|
||||||
|
#[cfg(feature = "foo")]
|
||||||
|
pub(crate) fn new(my_val: usize, bar: bool) -> Self {
|
||||||
|
Self { my_val, bar }
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "foo"))]
|
||||||
|
pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
|
||||||
|
Self { my_val }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_such_field_enum_with_feature_flag_diagnostics() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- /lib.rs crate:foo cfg:feature=foo
|
||||||
|
enum Foo {
|
||||||
|
#[cfg(not(feature = "foo"))]
|
||||||
|
Buz,
|
||||||
|
#[cfg(feature = "foo")]
|
||||||
|
Bar,
|
||||||
|
Baz
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_fn(f: Foo) {
|
||||||
|
match f {
|
||||||
|
Foo::Bar => {},
|
||||||
|
Foo::Baz => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- /lib.rs crate:foo cfg:feature=foo
|
||||||
|
struct S {
|
||||||
|
#[cfg(feature = "foo")]
|
||||||
|
foo: u32,
|
||||||
|
#[cfg(not(feature = "foo"))]
|
||||||
|
bar: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl S {
|
||||||
|
#[cfg(feature = "foo")]
|
||||||
|
fn new(foo: u32) -> Self {
|
||||||
|
Self { foo }
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "foo"))]
|
||||||
|
fn new(bar: u32) -> Self {
|
||||||
|
Self { bar }
|
||||||
|
}
|
||||||
|
fn new2(bar: u32) -> Self {
|
||||||
|
#[cfg(feature = "foo")]
|
||||||
|
{ Self { foo: bar } }
|
||||||
|
#[cfg(not(feature = "foo"))]
|
||||||
|
{ Self { bar } }
|
||||||
|
}
|
||||||
|
fn new2(val: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
#[cfg(feature = "foo")]
|
||||||
|
foo: val,
|
||||||
|
#[cfg(not(feature = "foo"))]
|
||||||
|
bar: val,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_such_field_with_type_macro() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
macro_rules! Type { () => { u32 }; }
|
||||||
|
struct Foo { bar: Type![] }
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn new() -> Self {
|
||||||
|
Foo { bar: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user