From d3621eeb02652038a8185f60d78fb4791a732dc6 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 13 Jun 2021 19:35:30 +0300 Subject: internal: refactor unimplemented builtin macro diagnostic --- crates/hir/src/diagnostics.rs | 30 +++++----------------- crates/hir/src/lib.rs | 8 ++++-- crates/ide/src/diagnostics.rs | 7 ++--- .../src/diagnostics/unimplemented_builtin_macro.rs | 19 ++++++++++++++ 4 files changed, 33 insertions(+), 31 deletions(-) create mode 100644 crates/ide/src/diagnostics/unimplemented_builtin_macro.rs diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 28580eeb4..3908e67a2 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -32,14 +32,15 @@ macro_rules! diagnostics { } diagnostics![ - UnresolvedModule, + InactiveCode, + MacroError, + MissingFields, + UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, + UnresolvedModule, UnresolvedProcMacro, - MacroError, - MissingFields, - InactiveCode, ]; #[derive(Debug)] @@ -88,26 +89,7 @@ pub struct MacroError { #[derive(Debug)] pub struct UnimplementedBuiltinMacro { - pub file: HirFileId, - pub node: SyntaxNodePtr, -} - -impl Diagnostic for UnimplementedBuiltinMacro { - fn code(&self) -> DiagnosticCode { - DiagnosticCode("unimplemented-builtin-macro") - } - - fn message(&self) -> String { - "unimplemented built-in macro".to_string() - } - - fn display_source(&self) -> InFile { - InFile::new(self.file, self.node.clone()) - } - - fn as_any(&self) -> &(dyn Any + Send + 'static) { - self - } + pub node: InFile, } // Diagnostic: no-such-field diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d891d0ec1..a361158e0 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -606,8 +606,12 @@ impl Module { let node = ast.to_node(db.upcast()); // Must have a name, otherwise we wouldn't emit it. let name = node.name().expect("unimplemented builtin macro with no name"); - let ptr = SyntaxNodePtr::from(AstPtr::new(&name)); - sink.push(UnimplementedBuiltinMacro { file: ast.file_id, node: ptr }); + acc.push( + UnimplementedBuiltinMacro { + node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&name))), + } + .into(), + ); } } } diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index aeccf1164..3fbd21c30 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -9,6 +9,7 @@ mod unresolved_extern_crate; mod unresolved_import; mod unresolved_macro_call; mod unresolved_proc_macro; +mod unimplemented_builtin_macro; mod macro_error; mod inactive_code; mod missing_fields; @@ -185,11 +186,6 @@ pub(crate) fn diagnostics( .with_code(Some(d.code())), ); }) - .on::(|d| { - let display_range = sema.diagnostics_display_range(d.display_source()).range; - res.borrow_mut() - .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code()))); - }) // Only collect experimental diagnostics when they're enabled. .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) .filter(|diag| !config.disabled.contains(diag.code().as_str())); @@ -229,6 +225,7 @@ pub(crate) fn diagnostics( AnyDiagnostic::UnresolvedImport(d) => unresolved_import::unresolved_import(&ctx, &d), AnyDiagnostic::UnresolvedMacroCall(d) => unresolved_macro_call::unresolved_macro_call(&ctx, &d), AnyDiagnostic::UnresolvedProcMacro(d) => unresolved_proc_macro::unresolved_proc_macro(&ctx, &d), + AnyDiagnostic::UnimplementedBuiltinMacro(d) => unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d), AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d), AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d), diff --git a/crates/ide/src/diagnostics/unimplemented_builtin_macro.rs b/crates/ide/src/diagnostics/unimplemented_builtin_macro.rs new file mode 100644 index 000000000..09faa3bbc --- /dev/null +++ b/crates/ide/src/diagnostics/unimplemented_builtin_macro.rs @@ -0,0 +1,19 @@ +use crate::{ + diagnostics::{Diagnostic, DiagnosticsContext}, + Severity, +}; + +// Diagnostic: unimplemented-builtin-macro +// +// This diagnostic is shown for builtin macros which are not yet implemented by rust-analyzer +pub(super) fn unimplemented_builtin_macro( + ctx: &DiagnosticsContext<'_>, + d: &hir::UnimplementedBuiltinMacro, +) -> Diagnostic { + Diagnostic::new( + "unimplemented-builtin-macro", + "unimplemented built-in macro".to_string(), + ctx.sema.diagnostics_display_range(d.node.clone()).range, + ) + .severity(Severity::WeakWarning) +} -- cgit v1.2.3 From 7166e8549bad95b05f66acd508d07a6cd97d3dc0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 13 Jun 2021 19:45:16 +0300 Subject: internal: refactor NoSuchField diagnostic --- crates/hir/src/diagnostics.rs | 25 +- crates/hir/src/lib.rs | 2 +- crates/ide/src/diagnostics.rs | 146 +----------- crates/ide/src/diagnostics/fixes/create_field.rs | 155 ------------ crates/ide/src/diagnostics/no_such_field.rs | 286 +++++++++++++++++++++++ 5 files changed, 300 insertions(+), 314 deletions(-) create mode 100644 crates/ide/src/diagnostics/no_such_field.rs diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 3908e67a2..c7702e09f 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -35,6 +35,7 @@ diagnostics![ InactiveCode, MacroError, MissingFields, + NoSuchField, UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, @@ -92,31 +93,9 @@ pub struct UnimplementedBuiltinMacro { pub node: InFile, } -// 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, -} - -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 { - InFile::new(self.file, self.field.clone().into()) - } - - fn as_any(&self) -> &(dyn Any + Send + 'static) { - self - } + pub field: InFile>, } // Diagnostic: break-outside-of-loop diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index a361158e0..7faf6ec1f 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1077,7 +1077,7 @@ impl Function { match d { hir_ty::InferenceDiagnostic::NoSuchField { expr } => { let field = source_map.field_syntax(*expr); - sink.push(NoSuchField { file: field.file_id, field: field.value }) + acc.push(NoSuchField { field }.into()) } hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr } => { let ptr = source_map diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 3fbd21c30..caaa89e0a 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -4,15 +4,16 @@ //! macro-expanded files, but we need to present them to the users in terms of //! original files. So we need to map the ranges. -mod unresolved_module; +mod inactive_code; +mod macro_error; +mod missing_fields; +mod no_such_field; +mod unimplemented_builtin_macro; mod unresolved_extern_crate; mod unresolved_import; mod unresolved_macro_call; +mod unresolved_module; mod unresolved_proc_macro; -mod unimplemented_builtin_macro; -mod macro_error; -mod inactive_code; -mod missing_fields; mod fixes; mod field_shorthand; @@ -161,9 +162,6 @@ pub(crate) fn diagnostics( .on::(|d| { res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); }) - .on::(|d| { - res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); - }) .on::(|d| { res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); }) @@ -220,14 +218,15 @@ pub(crate) fn diagnostics( for diag in diags { #[rustfmt::skip] let d = match diag { - AnyDiagnostic::UnresolvedModule(d) => unresolved_module::unresolved_module(&ctx, &d), + AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d), + AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d), + AnyDiagnostic::NoSuchField(d) => no_such_field::no_such_field(&ctx, &d), + AnyDiagnostic::UnimplementedBuiltinMacro(d) => unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d), AnyDiagnostic::UnresolvedExternCrate(d) => unresolved_extern_crate::unresolved_extern_crate(&ctx, &d), AnyDiagnostic::UnresolvedImport(d) => unresolved_import::unresolved_import(&ctx, &d), AnyDiagnostic::UnresolvedMacroCall(d) => unresolved_macro_call::unresolved_macro_call(&ctx, &d), + AnyDiagnostic::UnresolvedModule(d) => unresolved_module::unresolved_module(&ctx, &d), AnyDiagnostic::UnresolvedProcMacro(d) => unresolved_proc_macro::unresolved_proc_macro(&ctx, &d), - AnyDiagnostic::UnimplementedBuiltinMacro(d) => unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d), - AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d), - AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d), AnyDiagnostic::InactiveCode(d) => match inactive_code::inactive_code(&ctx, &d) { Some(it) => it, @@ -722,129 +721,6 @@ fn foo() { break; } ); } - #[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] fn missing_unsafe_diagnostic_with_raw_ptr() { check_diagnostics( diff --git a/crates/ide/src/diagnostics/fixes/create_field.rs b/crates/ide/src/diagnostics/fixes/create_field.rs index f6e45967a..8b1378917 100644 --- a/crates/ide/src/diagnostics/fixes/create_field.rs +++ b/crates/ide/src/diagnostics/fixes/create_field.rs @@ -1,156 +1 @@ -use hir::{db::AstDatabase, diagnostics::NoSuchField, HasSource, HirDisplay, Semantics}; -use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase}; -use syntax::{ - ast::{self, edit::IndentLevel, make}, - AstNode, -}; -use text_edit::TextEdit; -use crate::{ - diagnostics::{fix, DiagnosticWithFixes}, - Assist, AssistResolveStrategy, -}; -impl DiagnosticWithFixes for NoSuchField { - fn fixes( - &self, - sema: &Semantics, - _resolve: &AssistResolveStrategy, - ) -> Option> { - let root = sema.db.parse_or_expand(self.file)?; - missing_record_expr_field_fixes( - sema, - self.file.original_file(sema.db), - &self.field.to_node(&root), - ) - } -} - -fn missing_record_expr_field_fixes( - sema: &Semantics, - usage_file_id: FileId, - record_expr_field: &ast::RecordExprField, -) -> Option> { - let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?; - let def_id = sema.resolve_variant(record_lit)?; - let module; - let def_file_id; - let record_fields = match def_id { - hir::VariantDef::Struct(s) => { - module = s.module(sema.db); - let source = s.source(sema.db)?; - def_file_id = source.file_id; - let fields = source.value.field_list()?; - record_field_list(fields)? - } - hir::VariantDef::Union(u) => { - module = u.module(sema.db); - let source = u.source(sema.db)?; - def_file_id = source.file_id; - source.value.record_field_list()? - } - hir::VariantDef::Variant(e) => { - module = e.module(sema.db); - let source = e.source(sema.db)?; - def_file_id = source.file_id; - let fields = source.value.field_list()?; - record_field_list(fields)? - } - }; - let def_file_id = def_file_id.original_file(sema.db); - - let new_field_type = sema.type_of_expr(&record_expr_field.expr()?)?; - if new_field_type.is_unknown() { - return None; - } - let new_field = make::record_field( - None, - make::name(&record_expr_field.field_name()?.text()), - make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), - ); - - let last_field = record_fields.fields().last()?; - let last_field_syntax = last_field.syntax(); - let indent = IndentLevel::from_node(last_field_syntax); - - let mut new_field = new_field.to_string(); - if usage_file_id != def_file_id { - new_field = format!("pub(crate) {}", new_field); - } - new_field = format!("\n{}{}", indent, new_field); - - let needs_comma = !last_field_syntax.to_string().ends_with(','); - if needs_comma { - new_field = format!(",{}", new_field); - } - - let source_change = SourceChange::from_text_edit( - def_file_id, - TextEdit::insert(last_field_syntax.text_range().end(), new_field), - ); - - return Some(vec![fix( - "create_field", - "Create field", - source_change, - record_expr_field.syntax().text_range(), - )]); - - fn record_field_list(field_def_list: ast::FieldList) -> Option { - match field_def_list { - ast::FieldList::RecordFieldList(it) => Some(it), - ast::FieldList::TupleFieldList(_) => None, - } - } -} - -#[cfg(test)] -mod tests { - use crate::diagnostics::tests::check_fix; - - #[test] - fn test_add_field_from_usage() { - check_fix( - r" -fn main() { - Foo { bar: 3, baz$0: false}; -} -struct Foo { - bar: i32 -} -", - r" -fn main() { - Foo { bar: 3, baz: false}; -} -struct Foo { - bar: i32, - baz: bool -} -", - ) - } - - #[test] - fn test_add_field_in_other_file_from_usage() { - check_fix( - r#" -//- /main.rs -mod foo; - -fn main() { - foo::Foo { bar: 3, $0baz: false}; -} -//- /foo.rs -struct Foo { - bar: i32 -} -"#, - r#" -struct Foo { - bar: i32, - pub(crate) baz: bool -} -"#, - ) - } -} diff --git a/crates/ide/src/diagnostics/no_such_field.rs b/crates/ide/src/diagnostics/no_such_field.rs new file mode 100644 index 000000000..61962de28 --- /dev/null +++ b/crates/ide/src/diagnostics/no_such_field.rs @@ -0,0 +1,286 @@ +use hir::{db::AstDatabase, HasSource, HirDisplay, Semantics}; +use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase}; +use syntax::{ + ast::{self, edit::IndentLevel, make}, + AstNode, +}; +use text_edit::TextEdit; + +use crate::{ + diagnostics::{fix, Diagnostic, DiagnosticsContext}, + Assist, +}; + +// Diagnostic: no-such-field +// +// This diagnostic is triggered if created structure does not have field provided in record. +pub(super) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic { + Diagnostic::new( + "no-such-field", + "no such field".to_string(), + ctx.sema.diagnostics_display_range(d.field.clone().map(|it| it.into())).range, + ) + .with_fixes(fixes(ctx, d)) +} + +fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option> { + let root = ctx.sema.db.parse_or_expand(d.field.file_id)?; + missing_record_expr_field_fixes( + &ctx.sema, + d.field.file_id.original_file(ctx.sema.db), + &d.field.value.to_node(&root), + ) +} + +fn missing_record_expr_field_fixes( + sema: &Semantics, + usage_file_id: FileId, + record_expr_field: &ast::RecordExprField, +) -> Option> { + let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?; + let def_id = sema.resolve_variant(record_lit)?; + let module; + let def_file_id; + let record_fields = match def_id { + hir::VariantDef::Struct(s) => { + module = s.module(sema.db); + let source = s.source(sema.db)?; + def_file_id = source.file_id; + let fields = source.value.field_list()?; + record_field_list(fields)? + } + hir::VariantDef::Union(u) => { + module = u.module(sema.db); + let source = u.source(sema.db)?; + def_file_id = source.file_id; + source.value.record_field_list()? + } + hir::VariantDef::Variant(e) => { + module = e.module(sema.db); + let source = e.source(sema.db)?; + def_file_id = source.file_id; + let fields = source.value.field_list()?; + record_field_list(fields)? + } + }; + let def_file_id = def_file_id.original_file(sema.db); + + let new_field_type = sema.type_of_expr(&record_expr_field.expr()?)?; + if new_field_type.is_unknown() { + return None; + } + let new_field = make::record_field( + None, + make::name(&record_expr_field.field_name()?.text()), + make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), + ); + + let last_field = record_fields.fields().last()?; + let last_field_syntax = last_field.syntax(); + let indent = IndentLevel::from_node(last_field_syntax); + + let mut new_field = new_field.to_string(); + if usage_file_id != def_file_id { + new_field = format!("pub(crate) {}", new_field); + } + new_field = format!("\n{}{}", indent, new_field); + + let needs_comma = !last_field_syntax.to_string().ends_with(','); + if needs_comma { + new_field = format!(",{}", new_field); + } + + let source_change = SourceChange::from_text_edit( + def_file_id, + TextEdit::insert(last_field_syntax.text_range().end(), new_field), + ); + + return Some(vec![fix( + "create_field", + "Create field", + source_change, + record_expr_field.syntax().text_range(), + )]); + + fn record_field_list(field_def_list: ast::FieldList) -> Option { + match field_def_list { + ast::FieldList::RecordFieldList(it) => Some(it), + ast::FieldList::TupleFieldList(_) => None, + } + } +} + +#[cfg(test)] +mod tests { + use crate::diagnostics::tests::{check_diagnostics, check_fix}; + + #[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] + fn test_add_field_from_usage() { + check_fix( + r" +fn main() { + Foo { bar: 3, baz$0: false}; +} +struct Foo { + bar: i32 +} +", + r" +fn main() { + Foo { bar: 3, baz: false}; +} +struct Foo { + bar: i32, + baz: bool +} +", + ) + } + + #[test] + fn test_add_field_in_other_file_from_usage() { + check_fix( + r#" +//- /main.rs +mod foo; + +fn main() { + foo::Foo { bar: 3, $0baz: false}; +} +//- /foo.rs +struct Foo { + bar: i32 +} +"#, + r#" +struct Foo { + bar: i32, + pub(crate) baz: bool +} +"#, + ) + } +} -- cgit v1.2.3 From 886b66cd03cbe7cb13e248d7c7bbdeba66c7796a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 13 Jun 2021 19:51:19 +0300 Subject: internal: refactor BreakOutsideOfLoop diagnostic --- crates/hir/src/diagnostics.rs | 22 ++-------------- crates/hir/src/lib.rs | 4 +-- crates/ide/src/diagnostics.rs | 12 ++------- .../ide/src/diagnostics/break_outside_of_loop.rs | 30 ++++++++++++++++++++++ 4 files changed, 36 insertions(+), 32 deletions(-) create mode 100644 crates/ide/src/diagnostics/break_outside_of_loop.rs diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index c7702e09f..47d17ba70 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -32,6 +32,7 @@ macro_rules! diagnostics { } diagnostics![ + BreakOutsideOfLoop, InactiveCode, MacroError, MissingFields, @@ -98,28 +99,9 @@ pub struct NoSuchField { pub field: InFile>, } -// 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, -} - -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 { - InFile { file_id: self.file, value: self.expr.clone().into() } - } - fn as_any(&self) -> &(dyn Any + Send + 'static) { - self - } + pub expr: InFile>, } // Diagnostic: missing-unsafe diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 7faf6ec1f..2f507b83b 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1080,10 +1080,10 @@ impl Function { acc.push(NoSuchField { field }.into()) } hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr } => { - let ptr = source_map + let expr = source_map .expr_syntax(*expr) .expect("break outside of loop in synthetic syntax"); - sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value }) + acc.push(BreakOutsideOfLoop { expr }.into()) } } } diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index caaa89e0a..e8f22c889 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -4,6 +4,7 @@ //! macro-expanded files, but we need to present them to the users in terms of //! original files. So we need to map the ranges. +mod break_outside_of_loop; mod inactive_code; mod macro_error; mod missing_fields; @@ -218,6 +219,7 @@ pub(crate) fn diagnostics( for diag in diags { #[rustfmt::skip] let d = match diag { + AnyDiagnostic::BreakOutsideOfLoop(d) => break_outside_of_loop::break_outside_of_loop(&ctx, &d), AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d), AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d), AnyDiagnostic::NoSuchField(d) => no_such_field::no_such_field(&ctx, &d), @@ -711,16 +713,6 @@ mod foo; ); } - #[test] - fn break_outside_of_loop() { - check_diagnostics( - r#" -fn foo() { break; } - //^^^^^ break outside of loop -"#, - ); - } - #[test] fn missing_unsafe_diagnostic_with_raw_ptr() { check_diagnostics( diff --git a/crates/ide/src/diagnostics/break_outside_of_loop.rs b/crates/ide/src/diagnostics/break_outside_of_loop.rs new file mode 100644 index 000000000..80e68f3cc --- /dev/null +++ b/crates/ide/src/diagnostics/break_outside_of_loop.rs @@ -0,0 +1,30 @@ +use crate::diagnostics::{Diagnostic, DiagnosticsContext}; + +// Diagnostic: break-outside-of-loop +// +// This diagnostic is triggered if the `break` keyword is used outside of a loop. +pub(super) fn break_outside_of_loop( + ctx: &DiagnosticsContext<'_>, + d: &hir::BreakOutsideOfLoop, +) -> Diagnostic { + Diagnostic::new( + "break-outside-of-loop", + "break outside of loop", + ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + ) +} + +#[cfg(test)] +mod tests { + use crate::diagnostics::tests::check_diagnostics; + + #[test] + fn break_outside_of_loop() { + check_diagnostics( + r#" +fn foo() { break; } + //^^^^^ break outside of loop +"#, + ); + } +} -- cgit v1.2.3 From bccf77f26cd504de14f7d7d03f9f2a85d0fabb3d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 13 Jun 2021 20:00:27 +0300 Subject: internal: refactor missing unsafe diagnostic --- crates/hir/src/diagnostics.rs | 22 +----- crates/hir/src/lib.rs | 4 +- crates/ide/src/diagnostics.rs | 86 +---------------------- crates/ide/src/diagnostics/missing_unsafe.rs | 101 +++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 107 deletions(-) create mode 100644 crates/ide/src/diagnostics/missing_unsafe.rs diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 47d17ba70..f7bf63215 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -36,6 +36,7 @@ diagnostics![ InactiveCode, MacroError, MissingFields, + MissingUnsafe, NoSuchField, UnimplementedBuiltinMacro, UnresolvedExternCrate, @@ -104,28 +105,9 @@ pub struct BreakOutsideOfLoop { pub expr: InFile>, } -// Diagnostic: missing-unsafe -// -// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. #[derive(Debug)] pub struct MissingUnsafe { - pub file: HirFileId, - pub expr: AstPtr, -} - -impl Diagnostic for MissingUnsafe { - fn code(&self) -> DiagnosticCode { - DiagnosticCode("missing-unsafe") - } - fn message(&self) -> String { - format!("This operation is unsafe and requires an unsafe function or block") - } - fn display_source(&self) -> InFile { - InFile { file_id: self.file, value: self.expr.clone().into() } - } - fn as_any(&self) -> &(dyn Any + Send + 'static) { - self - } + pub expr: InFile>, } #[derive(Debug)] diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 2f507b83b..16f862707 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1090,9 +1090,7 @@ impl Function { for expr in hir_ty::diagnostics::missing_unsafe(db, self.id.into()) { match source_map.expr_syntax(expr) { - Ok(in_file) => { - sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value }) - } + Ok(expr) => acc.push(MissingUnsafe { expr }.into()), Err(SyntheticSyntax) => { // FIXME: Here and eslwhere in this file, the `expr` was // desugared, report or assert that this doesn't happen. diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index e8f22c889..67390345f 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -8,6 +8,7 @@ mod break_outside_of_loop; mod inactive_code; mod macro_error; mod missing_fields; +mod missing_unsafe; mod no_such_field; mod unimplemented_builtin_macro; mod unresolved_extern_crate; @@ -222,6 +223,7 @@ pub(crate) fn diagnostics( AnyDiagnostic::BreakOutsideOfLoop(d) => break_outside_of_loop::break_outside_of_loop(&ctx, &d), AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d), AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d), + AnyDiagnostic::MissingUnsafe(d) => missing_unsafe::missing_unsafe(&ctx, &d), AnyDiagnostic::NoSuchField(d) => no_such_field::no_such_field(&ctx, &d), AnyDiagnostic::UnimplementedBuiltinMacro(d) => unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d), AnyDiagnostic::UnresolvedExternCrate(d) => unresolved_extern_crate::unresolved_extern_crate(&ctx, &d), @@ -713,90 +715,6 @@ mod foo; ); } - #[test] - fn missing_unsafe_diagnostic_with_raw_ptr() { - check_diagnostics( - r#" -fn main() { - let x = &5 as *const usize; - unsafe { let y = *x; } - let z = *x; -} //^^ This operation is unsafe and requires an unsafe function or block -"#, - ) - } - - #[test] - fn missing_unsafe_diagnostic_with_unsafe_call() { - check_diagnostics( - r#" -struct HasUnsafe; - -impl HasUnsafe { - unsafe fn unsafe_fn(&self) { - let x = &5 as *const usize; - let y = *x; - } -} - -unsafe fn unsafe_fn() { - let x = &5 as *const usize; - let y = *x; -} - -fn main() { - unsafe_fn(); - //^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block - HasUnsafe.unsafe_fn(); - //^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block - unsafe { - unsafe_fn(); - HasUnsafe.unsafe_fn(); - } -} -"#, - ); - } - - #[test] - fn missing_unsafe_diagnostic_with_static_mut() { - check_diagnostics( - r#" -struct Ty { - a: u8, -} - -static mut STATIC_MUT: Ty = Ty { a: 0 }; - -fn main() { - let x = STATIC_MUT.a; - //^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block - unsafe { - let x = STATIC_MUT.a; - } -} -"#, - ); - } - - #[test] - fn no_missing_unsafe_diagnostic_with_safe_intrinsic() { - check_diagnostics( - r#" -extern "rust-intrinsic" { - pub fn bitreverse(x: u32) -> u32; // Safe intrinsic - pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic -} - -fn main() { - let _ = bitreverse(12); - let _ = floorf32(12.0); - //^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block -} -"#, - ); - } - // Register the required standard library types to make the tests work fn add_filter_map_with_find_next_boilerplate(body: &str) -> String { let prefix = r#" diff --git a/crates/ide/src/diagnostics/missing_unsafe.rs b/crates/ide/src/diagnostics/missing_unsafe.rs new file mode 100644 index 000000000..5c47e8d0a --- /dev/null +++ b/crates/ide/src/diagnostics/missing_unsafe.rs @@ -0,0 +1,101 @@ +use crate::diagnostics::{Diagnostic, DiagnosticsContext}; + +// Diagnostic: missing-unsafe +// +// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. +pub(super) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic { + Diagnostic::new( + "missing-unsafe", + "this operation is unsafe and requires an unsafe function or block", + ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + ) +} + +#[cfg(test)] +mod tests { + use crate::diagnostics::tests::check_diagnostics; + + #[test] + fn missing_unsafe_diagnostic_with_raw_ptr() { + check_diagnostics( + r#" +fn main() { + let x = &5 as *const usize; + unsafe { let y = *x; } + let z = *x; +} //^^ this operation is unsafe and requires an unsafe function or block +"#, + ) + } + + #[test] + fn missing_unsafe_diagnostic_with_unsafe_call() { + check_diagnostics( + r#" +struct HasUnsafe; + +impl HasUnsafe { + unsafe fn unsafe_fn(&self) { + let x = &5 as *const usize; + let y = *x; + } +} + +unsafe fn unsafe_fn() { + let x = &5 as *const usize; + let y = *x; +} + +fn main() { + unsafe_fn(); + //^^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block + HasUnsafe.unsafe_fn(); + //^^^^^^^^^^^^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block + unsafe { + unsafe_fn(); + HasUnsafe.unsafe_fn(); + } +} +"#, + ); + } + + #[test] + fn missing_unsafe_diagnostic_with_static_mut() { + check_diagnostics( + r#" +struct Ty { + a: u8, +} + +static mut STATIC_MUT: Ty = Ty { a: 0 }; + +fn main() { + let x = STATIC_MUT.a; + //^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block + unsafe { + let x = STATIC_MUT.a; + } +} +"#, + ); + } + + #[test] + fn no_missing_unsafe_diagnostic_with_safe_intrinsic() { + check_diagnostics( + r#" +extern "rust-intrinsic" { + pub fn bitreverse(x: u32) -> u32; // Safe intrinsic + pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic +} + +fn main() { + let _ = bitreverse(12); + let _ = floorf32(12.0); + //^^^^^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block +} +"#, + ); + } +} -- cgit v1.2.3 From 8d391ec981562785ec92ce3afe950972c523f925 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 13 Jun 2021 20:06:25 +0300 Subject: internal: refactor mismatched args count diagnostic --- crates/hir/src/diagnostics.rs | 26 +- crates/hir/src/lib.rs | 9 +- crates/ide/src/diagnostics.rs | 252 +------------------ crates/ide/src/diagnostics/mismatched_arg_count.rs | 272 +++++++++++++++++++++ 4 files changed, 279 insertions(+), 280 deletions(-) create mode 100644 crates/ide/src/diagnostics/mismatched_arg_count.rs diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index f7bf63215..f839616ce 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -35,6 +35,7 @@ diagnostics![ BreakOutsideOfLoop, InactiveCode, MacroError, + MismatchedArgCount, MissingFields, MissingUnsafe, NoSuchField, @@ -143,36 +144,13 @@ impl Diagnostic for ReplaceFilterMapNextWithFindMap { } } -// Diagnostic: mismatched-arg-count -// -// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. #[derive(Debug)] pub struct MismatchedArgCount { - pub file: HirFileId, - pub call_expr: AstPtr, + pub call_expr: InFile>, pub expected: usize, pub found: usize, } -impl Diagnostic for MismatchedArgCount { - fn code(&self) -> DiagnosticCode { - DiagnosticCode("mismatched-arg-count") - } - fn message(&self) -> String { - let s = if self.expected == 1 { "" } else { "s" }; - format!("Expected {} argument{}, found {}", self.expected, s, self.found) - } - fn display_source(&self) -> InFile { - InFile { file_id: self.file, value: self.call_expr.clone().into() } - } - fn as_any(&self) -> &(dyn Any + Send + 'static) { - self - } - fn is_experimental(&self) -> bool { - true - } -} - #[derive(Debug)] pub struct RemoveThisSemicolon { pub file: HirFileId, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 16f862707..c1af5f097 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1176,12 +1176,9 @@ impl Function { } BodyValidationDiagnostic::MismatchedArgCount { call_expr, expected, found } => { match source_map.expr_syntax(call_expr) { - Ok(source_ptr) => sink.push(MismatchedArgCount { - file: source_ptr.file_id, - call_expr: source_ptr.value, - expected, - found, - }), + Ok(source_ptr) => acc.push( + MismatchedArgCount { call_expr: source_ptr, expected, found }.into(), + ), Err(SyntheticSyntax) => (), } } diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 67390345f..4c92d0cf4 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -7,6 +7,7 @@ mod break_outside_of_loop; mod inactive_code; mod macro_error; +mod mismatched_arg_count; mod missing_fields; mod missing_unsafe; mod no_such_field; @@ -224,6 +225,7 @@ pub(crate) fn diagnostics( AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d), AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => missing_unsafe::missing_unsafe(&ctx, &d), + AnyDiagnostic::MismatchedArgCount(d) => mismatched_arg_count::mismatched_arg_count(&ctx, &d), AnyDiagnostic::NoSuchField(d) => no_such_field::no_such_field(&ctx, &d), AnyDiagnostic::UnimplementedBuiltinMacro(d) => unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d), AnyDiagnostic::UnresolvedExternCrate(d) => unresolved_extern_crate::unresolved_extern_crate(&ctx, &d), @@ -836,256 +838,6 @@ fn x(a: S) { ) } - #[test] - fn simple_free_fn_zero() { - check_diagnostics( - r#" -fn zero() {} -fn f() { zero(1); } - //^^^^^^^ Expected 0 arguments, found 1 -"#, - ); - - check_diagnostics( - r#" -fn zero() {} -fn f() { zero(); } -"#, - ); - } - - #[test] - fn simple_free_fn_one() { - check_diagnostics( - r#" -fn one(arg: u8) {} -fn f() { one(); } - //^^^^^ Expected 1 argument, found 0 -"#, - ); - - check_diagnostics( - r#" -fn one(arg: u8) {} -fn f() { one(1); } -"#, - ); - } - - #[test] - fn method_as_fn() { - check_diagnostics( - r#" -struct S; -impl S { fn method(&self) {} } - -fn f() { - S::method(); -} //^^^^^^^^^^^ Expected 1 argument, found 0 -"#, - ); - - check_diagnostics( - r#" -struct S; -impl S { fn method(&self) {} } - -fn f() { - S::method(&S); - S.method(); -} -"#, - ); - } - - #[test] - fn method_with_arg() { - check_diagnostics( - r#" -struct S; -impl S { fn method(&self, arg: u8) {} } - - fn f() { - S.method(); - } //^^^^^^^^^^ Expected 1 argument, found 0 - "#, - ); - - check_diagnostics( - r#" -struct S; -impl S { fn method(&self, arg: u8) {} } - -fn f() { - S::method(&S, 0); - S.method(1); -} -"#, - ); - } - - #[test] - fn method_unknown_receiver() { - // note: this is incorrect code, so there might be errors on this in the - // future, but we shouldn't emit an argument count diagnostic here - check_diagnostics( - r#" -trait Foo { fn method(&self, arg: usize) {} } - -fn f() { - let x; - x.method(); -} -"#, - ); - } - - #[test] - fn tuple_struct() { - check_diagnostics( - r#" -struct Tup(u8, u16); -fn f() { - Tup(0); -} //^^^^^^ Expected 2 arguments, found 1 -"#, - ) - } - - #[test] - fn enum_variant() { - check_diagnostics( - r#" -enum En { Variant(u8, u16), } -fn f() { - En::Variant(0); -} //^^^^^^^^^^^^^^ Expected 2 arguments, found 1 -"#, - ) - } - - #[test] - fn enum_variant_type_macro() { - check_diagnostics( - r#" -macro_rules! Type { - () => { u32 }; -} -enum Foo { - Bar(Type![]) -} -impl Foo { - fn new() { - Foo::Bar(0); - Foo::Bar(0, 1); - //^^^^^^^^^^^^^^ Expected 1 argument, found 2 - Foo::Bar(); - //^^^^^^^^^^ Expected 1 argument, found 0 - } -} - "#, - ); - } - - #[test] - fn varargs() { - check_diagnostics( - r#" -extern "C" { - fn fixed(fixed: u8); - fn varargs(fixed: u8, ...); - fn varargs2(...); -} - -fn f() { - unsafe { - fixed(0); - fixed(0, 1); - //^^^^^^^^^^^ Expected 1 argument, found 2 - varargs(0); - varargs(0, 1); - varargs2(); - varargs2(0); - varargs2(0, 1); - } -} - "#, - ) - } - - #[test] - fn arg_count_lambda() { - check_diagnostics( - r#" -fn main() { - let f = |()| (); - f(); - //^^^ Expected 1 argument, found 0 - f(()); - f((), ()); - //^^^^^^^^^ Expected 1 argument, found 2 -} -"#, - ) - } - - #[test] - fn cfgd_out_call_arguments() { - check_diagnostics( - r#" -struct C(#[cfg(FALSE)] ()); -impl C { - fn new() -> Self { - Self( - #[cfg(FALSE)] - (), - ) - } - - fn method(&self) {} -} - -fn main() { - C::new().method(#[cfg(FALSE)] 0); -} - "#, - ); - } - - #[test] - fn cfgd_out_fn_params() { - check_diagnostics( - r#" -fn foo(#[cfg(NEVER)] x: ()) {} - -struct S; - -impl S { - fn method(#[cfg(NEVER)] self) {} - fn method2(#[cfg(NEVER)] self, arg: u8) {} - fn method3(self, #[cfg(NEVER)] arg: u8) {} -} - -extern "C" { - fn fixed(fixed: u8, #[cfg(NEVER)] ...); - fn varargs(#[cfg(not(NEVER))] ...); -} - -fn main() { - foo(); - S::method(); - S::method2(0); - S::method3(S); - S.method3(); - unsafe { - fixed(0); - varargs(1, 2, 3); - } -} - "#, - ) - } - #[test] fn missing_semicolon() { check_diagnostics( diff --git a/crates/ide/src/diagnostics/mismatched_arg_count.rs b/crates/ide/src/diagnostics/mismatched_arg_count.rs new file mode 100644 index 000000000..08e1cfa5f --- /dev/null +++ b/crates/ide/src/diagnostics/mismatched_arg_count.rs @@ -0,0 +1,272 @@ +use crate::diagnostics::{Diagnostic, DiagnosticsContext}; + +// Diagnostic: mismatched-arg-count +// +// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. +pub(super) fn mismatched_arg_count( + ctx: &DiagnosticsContext<'_>, + d: &hir::MismatchedArgCount, +) -> Diagnostic { + let s = if d.expected == 1 { "" } else { "s" }; + let message = format!("expected {} argument{}, found {}", d.expected, s, d.found); + Diagnostic::new( + "mismatched-arg-count", + message, + ctx.sema.diagnostics_display_range(d.call_expr.clone().map(|it| it.into())).range, + ) +} + +#[cfg(test)] +mod tests { + use crate::diagnostics::tests::check_diagnostics; + + #[test] + fn simple_free_fn_zero() { + check_diagnostics( + r#" +fn zero() {} +fn f() { zero(1); } + //^^^^^^^ expected 0 arguments, found 1 +"#, + ); + + check_diagnostics( + r#" +fn zero() {} +fn f() { zero(); } +"#, + ); + } + + #[test] + fn simple_free_fn_one() { + check_diagnostics( + r#" +fn one(arg: u8) {} +fn f() { one(); } + //^^^^^ expected 1 argument, found 0 +"#, + ); + + check_diagnostics( + r#" +fn one(arg: u8) {} +fn f() { one(1); } +"#, + ); + } + + #[test] + fn method_as_fn() { + check_diagnostics( + r#" +struct S; +impl S { fn method(&self) {} } + +fn f() { + S::method(); +} //^^^^^^^^^^^ expected 1 argument, found 0 +"#, + ); + + check_diagnostics( + r#" +struct S; +impl S { fn method(&self) {} } + +fn f() { + S::method(&S); + S.method(); +} +"#, + ); + } + + #[test] + fn method_with_arg() { + check_diagnostics( + r#" +struct S; +impl S { fn method(&self, arg: u8) {} } + + fn f() { + S.method(); + } //^^^^^^^^^^ expected 1 argument, found 0 + "#, + ); + + check_diagnostics( + r#" +struct S; +impl S { fn method(&self, arg: u8) {} } + +fn f() { + S::method(&S, 0); + S.method(1); +} +"#, + ); + } + + #[test] + fn method_unknown_receiver() { + // note: this is incorrect code, so there might be errors on this in the + // future, but we shouldn't emit an argument count diagnostic here + check_diagnostics( + r#" +trait Foo { fn method(&self, arg: usize) {} } + +fn f() { + let x; + x.method(); +} +"#, + ); + } + + #[test] + fn tuple_struct() { + check_diagnostics( + r#" +struct Tup(u8, u16); +fn f() { + Tup(0); +} //^^^^^^ expected 2 arguments, found 1 +"#, + ) + } + + #[test] + fn enum_variant() { + check_diagnostics( + r#" +enum En { Variant(u8, u16), } +fn f() { + En::Variant(0); +} //^^^^^^^^^^^^^^ expected 2 arguments, found 1 +"#, + ) + } + + #[test] + fn enum_variant_type_macro() { + check_diagnostics( + r#" +macro_rules! Type { + () => { u32 }; +} +enum Foo { + Bar(Type![]) +} +impl Foo { + fn new() { + Foo::Bar(0); + Foo::Bar(0, 1); + //^^^^^^^^^^^^^^ expected 1 argument, found 2 + Foo::Bar(); + //^^^^^^^^^^ expected 1 argument, found 0 + } +} + "#, + ); + } + + #[test] + fn varargs() { + check_diagnostics( + r#" +extern "C" { + fn fixed(fixed: u8); + fn varargs(fixed: u8, ...); + fn varargs2(...); +} + +fn f() { + unsafe { + fixed(0); + fixed(0, 1); + //^^^^^^^^^^^ expected 1 argument, found 2 + varargs(0); + varargs(0, 1); + varargs2(); + varargs2(0); + varargs2(0, 1); + } +} + "#, + ) + } + + #[test] + fn arg_count_lambda() { + check_diagnostics( + r#" +fn main() { + let f = |()| (); + f(); + //^^^ expected 1 argument, found 0 + f(()); + f((), ()); + //^^^^^^^^^ expected 1 argument, found 2 +} +"#, + ) + } + + #[test] + fn cfgd_out_call_arguments() { + check_diagnostics( + r#" +struct C(#[cfg(FALSE)] ()); +impl C { + fn new() -> Self { + Self( + #[cfg(FALSE)] + (), + ) + } + + fn method(&self) {} +} + +fn main() { + C::new().method(#[cfg(FALSE)] 0); +} + "#, + ); + } + + #[test] + fn cfgd_out_fn_params() { + check_diagnostics( + r#" +fn foo(#[cfg(NEVER)] x: ()) {} + +struct S; + +impl S { + fn method(#[cfg(NEVER)] self) {} + fn method2(#[cfg(NEVER)] self, arg: u8) {} + fn method3(self, #[cfg(NEVER)] arg: u8) {} +} + +extern "C" { + fn fixed(fixed: u8, #[cfg(NEVER)] ...); + fn varargs(#[cfg(not(NEVER))] ...); +} + +fn main() { + foo(); + S::method(); + S::method2(0); + S::method3(S); + S.method3(); + unsafe { + fixed(0); + varargs(1, 2, 3); + } +} + "#, + ) + } +} -- cgit v1.2.3