Skip to content

Commit 2ad7892

Browse files
bors[bot]matklad
andauthored
Merge #9253
9253: internal: refactor missing or or some diagnostic r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
2 parents 60ca03e + b66f4bb commit 2ad7892

12 files changed

+378
-415
lines changed

crates/hir/src/diagnostics.rs

Lines changed: 5 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@ diagnostics![
3737
MacroError,
3838
MismatchedArgCount,
3939
MissingFields,
40+
MissingOkOrSomeInTailExpr,
4041
MissingUnsafe,
4142
NoSuchField,
43+
RemoveThisSemicolon,
44+
ReplaceFilterMapNextWithFindMap,
4245
UnimplementedBuiltinMacro,
4346
UnresolvedExternCrate,
4447
UnresolvedImport,
@@ -119,31 +122,13 @@ pub struct MissingFields {
119122
pub missed_fields: Vec<Name>,
120123
}
121124

122-
// Diagnostic: replace-filter-map-next-with-find-map
123-
//
124-
// This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`.
125125
#[derive(Debug)]
126126
pub struct ReplaceFilterMapNextWithFindMap {
127127
pub file: HirFileId,
128128
/// This expression is the whole method chain up to and including `.filter_map(..).next()`.
129129
pub next_expr: AstPtr<ast::Expr>,
130130
}
131131

132-
impl Diagnostic for ReplaceFilterMapNextWithFindMap {
133-
fn code(&self) -> DiagnosticCode {
134-
DiagnosticCode("replace-filter-map-next-with-find-map")
135-
}
136-
fn message(&self) -> String {
137-
"replace filter_map(..).next() with find_map(..)".to_string()
138-
}
139-
fn display_source(&self) -> InFile<SyntaxNodePtr> {
140-
InFile { file_id: self.file, value: self.next_expr.clone().into() }
141-
}
142-
fn as_any(&self) -> &(dyn Any + Send + 'static) {
143-
self
144-
}
145-
}
146-
147132
#[derive(Debug)]
148133
pub struct MismatchedArgCount {
149134
pub call_expr: InFile<AstPtr<ast::Expr>>,
@@ -153,63 +138,16 @@ pub struct MismatchedArgCount {
153138

154139
#[derive(Debug)]
155140
pub struct RemoveThisSemicolon {
156-
pub file: HirFileId,
157-
pub expr: AstPtr<ast::Expr>,
158-
}
159-
160-
impl Diagnostic for RemoveThisSemicolon {
161-
fn code(&self) -> DiagnosticCode {
162-
DiagnosticCode("remove-this-semicolon")
163-
}
164-
165-
fn message(&self) -> String {
166-
"Remove this semicolon".to_string()
167-
}
168-
169-
fn display_source(&self) -> InFile<SyntaxNodePtr> {
170-
InFile { file_id: self.file, value: self.expr.clone().into() }
171-
}
172-
173-
fn as_any(&self) -> &(dyn Any + Send + 'static) {
174-
self
175-
}
141+
pub expr: InFile<AstPtr<ast::Expr>>,
176142
}
177143

178-
// Diagnostic: missing-ok-or-some-in-tail-expr
179-
//
180-
// This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`,
181-
// or if a block that should return `Option` returns a value not wrapped in `Some`.
182-
//
183-
// Example:
184-
//
185-
// ```rust
186-
// fn foo() -> Result<u8, ()> {
187-
// 10
188-
// }
189-
// ```
190144
#[derive(Debug)]
191145
pub struct MissingOkOrSomeInTailExpr {
192-
pub file: HirFileId,
193-
pub expr: AstPtr<ast::Expr>,
146+
pub expr: InFile<AstPtr<ast::Expr>>,
194147
// `Some` or `Ok` depending on whether the return type is Result or Option
195148
pub required: String,
196149
}
197150

198-
impl Diagnostic for MissingOkOrSomeInTailExpr {
199-
fn code(&self) -> DiagnosticCode {
200-
DiagnosticCode("missing-ok-or-some-in-tail-expr")
201-
}
202-
fn message(&self) -> String {
203-
format!("wrap return expression in {}", self.required)
204-
}
205-
fn display_source(&self) -> InFile<SyntaxNodePtr> {
206-
InFile { file_id: self.file, value: self.expr.clone().into() }
207-
}
208-
fn as_any(&self) -> &(dyn Any + Send + 'static) {
209-
self
210-
}
211-
}
212-
213151
// Diagnostic: missing-match-arm
214152
//
215153
// This diagnostic is triggered if `match` block is missing one or more match arms.

crates/hir/src/lib.rs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,10 +1168,13 @@ impl Function {
11681168
}
11691169
BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { method_call_expr } => {
11701170
if let Ok(next_source_ptr) = source_map.expr_syntax(method_call_expr) {
1171-
sink.push(ReplaceFilterMapNextWithFindMap {
1172-
file: next_source_ptr.file_id,
1173-
next_expr: next_source_ptr.value,
1174-
});
1171+
acc.push(
1172+
ReplaceFilterMapNextWithFindMap {
1173+
file: next_source_ptr.file_id,
1174+
next_expr: next_source_ptr.value,
1175+
}
1176+
.into(),
1177+
);
11751178
}
11761179
}
11771180
BodyValidationDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
@@ -1184,20 +1187,13 @@ impl Function {
11841187
}
11851188
BodyValidationDiagnostic::RemoveThisSemicolon { expr } => {
11861189
match source_map.expr_syntax(expr) {
1187-
Ok(source_ptr) => sink.push(RemoveThisSemicolon {
1188-
file: source_ptr.file_id,
1189-
expr: source_ptr.value,
1190-
}),
1190+
Ok(expr) => acc.push(RemoveThisSemicolon { expr }.into()),
11911191
Err(SyntheticSyntax) => (),
11921192
}
11931193
}
11941194
BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr, required } => {
11951195
match source_map.expr_syntax(expr) {
1196-
Ok(source_ptr) => sink.push(MissingOkOrSomeInTailExpr {
1197-
file: source_ptr.file_id,
1198-
expr: source_ptr.value,
1199-
required,
1200-
}),
1196+
Ok(expr) => acc.push(MissingOkOrSomeInTailExpr { expr, required }.into()),
12011197
Err(SyntheticSyntax) => (),
12021198
}
12031199
}

crates/ide/src/diagnostics.rs

Lines changed: 7 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ mod inactive_code;
99
mod macro_error;
1010
mod mismatched_arg_count;
1111
mod missing_fields;
12+
mod missing_ok_or_some_in_tail_expr;
1213
mod missing_unsafe;
1314
mod no_such_field;
15+
mod remove_this_semicolon;
16+
mod replace_filter_map_next_with_find_map;
1417
mod unimplemented_builtin_macro;
1518
mod unresolved_extern_crate;
1619
mod unresolved_import;
@@ -162,18 +165,9 @@ pub(crate) fn diagnostics(
162165
}
163166
let res = RefCell::new(res);
164167
let sink_builder = DiagnosticSinkBuilder::new()
165-
.on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| {
166-
res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
167-
})
168-
.on::<hir::diagnostics::RemoveThisSemicolon, _>(|d| {
169-
res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
170-
})
171168
.on::<hir::diagnostics::IncorrectCase, _>(|d| {
172169
res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
173170
})
174-
.on::<hir::diagnostics::ReplaceFilterMapNextWithFindMap, _>(|d| {
175-
res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
176-
})
177171
.on::<UnlinkedFile, _>(|d| {
178172
// Limit diagnostic to the first few characters in the file. This matches how VS Code
179173
// renders it with the full span, but on other editors, and is less invasive.
@@ -223,10 +217,13 @@ pub(crate) fn diagnostics(
223217
let d = match diag {
224218
AnyDiagnostic::BreakOutsideOfLoop(d) => break_outside_of_loop::break_outside_of_loop(&ctx, &d),
225219
AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d),
220+
AnyDiagnostic::MismatchedArgCount(d) => mismatched_arg_count::mismatched_arg_count(&ctx, &d),
226221
AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
222+
AnyDiagnostic::MissingOkOrSomeInTailExpr(d) => missing_ok_or_some_in_tail_expr::missing_ok_or_some_in_tail_expr(&ctx, &d),
227223
AnyDiagnostic::MissingUnsafe(d) => missing_unsafe::missing_unsafe(&ctx, &d),
228-
AnyDiagnostic::MismatchedArgCount(d) => mismatched_arg_count::mismatched_arg_count(&ctx, &d),
229224
AnyDiagnostic::NoSuchField(d) => no_such_field::no_such_field(&ctx, &d),
225+
AnyDiagnostic::RemoveThisSemicolon(d) => remove_this_semicolon::remove_this_semicolon(&ctx, &d),
226+
AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
230227
AnyDiagnostic::UnimplementedBuiltinMacro(d) => unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
231228
AnyDiagnostic::UnresolvedExternCrate(d) => unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
232229
AnyDiagnostic::UnresolvedImport(d) => unresolved_import::unresolved_import(&ctx, &d),
@@ -253,16 +250,6 @@ pub(crate) fn diagnostics(
253250
res
254251
}
255252

256-
fn diagnostic_with_fix<D: DiagnosticWithFixes>(
257-
d: &D,
258-
sema: &Semantics<RootDatabase>,
259-
resolve: &AssistResolveStrategy,
260-
) -> Diagnostic {
261-
Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message())
262-
.with_fixes(d.fixes(sema, resolve))
263-
.with_code(Some(d.code()))
264-
}
265-
266253
fn warning_with_fix<D: DiagnosticWithFixes>(
267254
d: &D,
268255
sema: &Semantics<RootDatabase>,
@@ -448,39 +435,6 @@ mod tests {
448435
}
449436
}
450437

451-
#[test]
452-
fn range_mapping_out_of_macros() {
453-
// FIXME: this is very wrong, but somewhat tricky to fix.
454-
check_fix(
455-
r#"
456-
fn some() {}
457-
fn items() {}
458-
fn here() {}
459-
460-
macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
461-
462-
fn main() {
463-
let _x = id![Foo { a: $042 }];
464-
}
465-
466-
pub struct Foo { pub a: i32, pub b: i32 }
467-
"#,
468-
r#"
469-
fn some(, b: () ) {}
470-
fn items() {}
471-
fn here() {}
472-
473-
macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
474-
475-
fn main() {
476-
let _x = id![Foo { a: 42 }];
477-
}
478-
479-
pub struct Foo { pub a: i32, pub b: i32 }
480-
"#,
481-
);
482-
}
483-
484438
#[test]
485439
fn test_check_unnecessary_braces_in_use_statement() {
486440
check_diagnostics(
@@ -717,137 +671,6 @@ mod foo;
717671
);
718672
}
719673

720-
// Register the required standard library types to make the tests work
721-
fn add_filter_map_with_find_next_boilerplate(body: &str) -> String {
722-
let prefix = r#"
723-
//- /main.rs crate:main deps:core
724-
use core::iter::Iterator;
725-
use core::option::Option::{self, Some, None};
726-
"#;
727-
let suffix = r#"
728-
//- /core/lib.rs crate:core
729-
pub mod option {
730-
pub enum Option<T> { Some(T), None }
731-
}
732-
pub mod iter {
733-
pub trait Iterator {
734-
type Item;
735-
fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap }
736-
fn next(&mut self) -> Option<Self::Item>;
737-
}
738-
pub struct FilterMap {}
739-
impl Iterator for FilterMap {
740-
type Item = i32;
741-
fn next(&mut self) -> i32 { 7 }
742-
}
743-
}
744-
"#;
745-
format!("{}{}{}", prefix, body, suffix)
746-
}
747-
748-
#[test]
749-
fn replace_filter_map_next_with_find_map2() {
750-
check_diagnostics(&add_filter_map_with_find_next_boilerplate(
751-
r#"
752-
fn foo() {
753-
let m = [1, 2, 3].iter().filter_map(|x| if *x == 2 { Some (4) } else { None }).next();
754-
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ replace filter_map(..).next() with find_map(..)
755-
}
756-
"#,
757-
));
758-
}
759-
760-
#[test]
761-
fn replace_filter_map_next_with_find_map_no_diagnostic_without_next() {
762-
check_diagnostics(&add_filter_map_with_find_next_boilerplate(
763-
r#"
764-
fn foo() {
765-
let m = [1, 2, 3]
766-
.iter()
767-
.filter_map(|x| if *x == 2 { Some (4) } else { None })
768-
.len();
769-
}
770-
"#,
771-
));
772-
}
773-
774-
#[test]
775-
fn replace_filter_map_next_with_find_map_no_diagnostic_with_intervening_methods() {
776-
check_diagnostics(&add_filter_map_with_find_next_boilerplate(
777-
r#"
778-
fn foo() {
779-
let m = [1, 2, 3]
780-
.iter()
781-
.filter_map(|x| if *x == 2 { Some (4) } else { None })
782-
.map(|x| x + 2)
783-
.len();
784-
}
785-
"#,
786-
));
787-
}
788-
789-
#[test]
790-
fn replace_filter_map_next_with_find_map_no_diagnostic_if_not_in_chain() {
791-
check_diagnostics(&add_filter_map_with_find_next_boilerplate(
792-
r#"
793-
fn foo() {
794-
let m = [1, 2, 3]
795-
.iter()
796-
.filter_map(|x| if *x == 2 { Some (4) } else { None });
797-
let n = m.next();
798-
}
799-
"#,
800-
));
801-
}
802-
803-
#[test]
804-
fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
805-
check_diagnostics(
806-
r"
807-
struct S { foo: i32, bar: () }
808-
fn baz(s: S) -> i32 {
809-
match s {
810-
S { foo, .. } => foo,
811-
}
812-
}
813-
",
814-
)
815-
}
816-
817-
#[test]
818-
fn missing_record_pat_field_box() {
819-
check_diagnostics(
820-
r"
821-
struct S { s: Box<u32> }
822-
fn x(a: S) {
823-
let S { box s } = a;
824-
}
825-
",
826-
)
827-
}
828-
829-
#[test]
830-
fn missing_record_pat_field_ref() {
831-
check_diagnostics(
832-
r"
833-
struct S { s: u32 }
834-
fn x(a: S) {
835-
let S { ref s } = a;
836-
}
837-
",
838-
)
839-
}
840-
841-
#[test]
842-
fn missing_semicolon() {
843-
check_diagnostics(
844-
r#"
845-
fn test() -> i32 { 123; }
846-
//^^^ Remove this semicolon
847-
"#,
848-
);
849-
}
850-
851674
#[test]
852675
fn import_extern_crate_clash_with_inner_item() {
853676
// This is more of a resolver test, but doesn't really work with the hir_def testsuite.

0 commit comments

Comments
 (0)