diff options
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics.rs | 240 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/expr.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/match_check.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/unsafe_check.rs | 50 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/test_db.rs | 50 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests.rs | 408 |
6 files changed, 278 insertions, 474 deletions
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index 3870c6d9c..d3ee9cf55 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs | |||
@@ -246,25 +246,233 @@ impl AstDiagnostic for MismatchedArgCount { | |||
246 | } | 246 | } |
247 | 247 | ||
248 | #[cfg(test)] | 248 | #[cfg(test)] |
249 | fn check_diagnostics(ra_fixture: &str) { | 249 | mod tests { |
250 | use ra_db::{fixture::WithFixture, FileId}; | 250 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; |
251 | use ra_syntax::TextRange; | 251 | use hir_expand::diagnostics::{Diagnostic, DiagnosticSink}; |
252 | use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; | ||
253 | use ra_syntax::{TextRange, TextSize}; | ||
252 | use rustc_hash::FxHashMap; | 254 | use rustc_hash::FxHashMap; |
253 | 255 | ||
254 | use crate::test_db::TestDB; | 256 | use crate::{diagnostics::validate_body, test_db::TestDB}; |
257 | |||
258 | impl TestDB { | ||
259 | fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) { | ||
260 | let crate_graph = self.crate_graph(); | ||
261 | for krate in crate_graph.iter() { | ||
262 | let crate_def_map = self.crate_def_map(krate); | ||
263 | |||
264 | let mut fns = Vec::new(); | ||
265 | for (module_id, _) in crate_def_map.modules.iter() { | ||
266 | for decl in crate_def_map[module_id].scope.declarations() { | ||
267 | if let ModuleDefId::FunctionId(f) = decl { | ||
268 | fns.push(f) | ||
269 | } | ||
270 | } | ||
271 | |||
272 | for impl_id in crate_def_map[module_id].scope.impls() { | ||
273 | let impl_data = self.impl_data(impl_id); | ||
274 | for item in impl_data.items.iter() { | ||
275 | if let AssocItemId::FunctionId(f) = item { | ||
276 | fns.push(*f) | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | } | ||
281 | |||
282 | for f in fns { | ||
283 | let mut sink = DiagnosticSink::new(&mut cb); | ||
284 | validate_body(self, f.into(), &mut sink); | ||
285 | } | ||
286 | } | ||
287 | } | ||
288 | } | ||
289 | |||
290 | pub(crate) fn check_diagnostics(ra_fixture: &str) { | ||
291 | let db = TestDB::with_files(ra_fixture); | ||
292 | let annotations = db.extract_annotations(); | ||
293 | |||
294 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | ||
295 | db.diagnostics(|d| { | ||
296 | // FXIME: macros... | ||
297 | let file_id = d.source().file_id.original_file(&db); | ||
298 | let range = d.syntax_node(&db).text_range(); | ||
299 | let message = d.message().to_owned(); | ||
300 | actual.entry(file_id).or_default().push((range, message)); | ||
301 | }); | ||
302 | |||
303 | for (file_id, diags) in actual.iter_mut() { | ||
304 | diags.sort_by_key(|it| it.0.start()); | ||
305 | let text = db.file_text(*file_id); | ||
306 | // For multiline spans, place them on line start | ||
307 | for (range, content) in diags { | ||
308 | if text[*range].contains('\n') { | ||
309 | *range = TextRange::new(range.start(), range.start() + TextSize::from(1)); | ||
310 | *content = format!("... {}", content); | ||
311 | } | ||
312 | } | ||
313 | } | ||
314 | |||
315 | assert_eq!(annotations, actual); | ||
316 | } | ||
317 | |||
318 | #[test] | ||
319 | fn no_such_field_diagnostics() { | ||
320 | check_diagnostics( | ||
321 | r#" | ||
322 | struct S { foo: i32, bar: () } | ||
323 | impl S { | ||
324 | fn new() -> S { | ||
325 | S { | ||
326 | //^... Missing structure fields: | ||
327 | //| - bar | ||
328 | foo: 92, | ||
329 | baz: 62, | ||
330 | //^^^^^^^ no such field | ||
331 | } | ||
332 | } | ||
333 | } | ||
334 | "#, | ||
335 | ); | ||
336 | } | ||
337 | #[test] | ||
338 | fn no_such_field_with_feature_flag_diagnostics() { | ||
339 | check_diagnostics( | ||
340 | r#" | ||
341 | //- /lib.rs crate:foo cfg:feature=foo | ||
342 | struct MyStruct { | ||
343 | my_val: usize, | ||
344 | #[cfg(feature = "foo")] | ||
345 | bar: bool, | ||
346 | } | ||
347 | |||
348 | impl MyStruct { | ||
349 | #[cfg(feature = "foo")] | ||
350 | pub(crate) fn new(my_val: usize, bar: bool) -> Self { | ||
351 | Self { my_val, bar } | ||
352 | } | ||
353 | #[cfg(not(feature = "foo"))] | ||
354 | pub(crate) fn new(my_val: usize, _bar: bool) -> Self { | ||
355 | Self { my_val } | ||
356 | } | ||
357 | } | ||
358 | "#, | ||
359 | ); | ||
360 | } | ||
361 | |||
362 | #[test] | ||
363 | fn no_such_field_enum_with_feature_flag_diagnostics() { | ||
364 | check_diagnostics( | ||
365 | r#" | ||
366 | //- /lib.rs crate:foo cfg:feature=foo | ||
367 | enum Foo { | ||
368 | #[cfg(not(feature = "foo"))] | ||
369 | Buz, | ||
370 | #[cfg(feature = "foo")] | ||
371 | Bar, | ||
372 | Baz | ||
373 | } | ||
374 | |||
375 | fn test_fn(f: Foo) { | ||
376 | match f { | ||
377 | Foo::Bar => {}, | ||
378 | Foo::Baz => {}, | ||
379 | } | ||
380 | } | ||
381 | "#, | ||
382 | ); | ||
383 | } | ||
384 | |||
385 | #[test] | ||
386 | fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { | ||
387 | check_diagnostics( | ||
388 | r#" | ||
389 | //- /lib.rs crate:foo cfg:feature=foo | ||
390 | struct S { | ||
391 | #[cfg(feature = "foo")] | ||
392 | foo: u32, | ||
393 | #[cfg(not(feature = "foo"))] | ||
394 | bar: u32, | ||
395 | } | ||
396 | |||
397 | impl S { | ||
398 | #[cfg(feature = "foo")] | ||
399 | fn new(foo: u32) -> Self { | ||
400 | Self { foo } | ||
401 | } | ||
402 | #[cfg(not(feature = "foo"))] | ||
403 | fn new(bar: u32) -> Self { | ||
404 | Self { bar } | ||
405 | } | ||
406 | fn new2(bar: u32) -> Self { | ||
407 | #[cfg(feature = "foo")] | ||
408 | { Self { foo: bar } } | ||
409 | #[cfg(not(feature = "foo"))] | ||
410 | { Self { bar } } | ||
411 | } | ||
412 | fn new2(val: u32) -> Self { | ||
413 | Self { | ||
414 | #[cfg(feature = "foo")] | ||
415 | foo: val, | ||
416 | #[cfg(not(feature = "foo"))] | ||
417 | bar: val, | ||
418 | } | ||
419 | } | ||
420 | } | ||
421 | "#, | ||
422 | ); | ||
423 | } | ||
255 | 424 | ||
256 | let db = TestDB::with_files(ra_fixture); | 425 | #[test] |
257 | let annotations = db.extract_annotations(); | 426 | fn no_such_field_with_type_macro() { |
427 | check_diagnostics( | ||
428 | r#" | ||
429 | macro_rules! Type { () => { u32 }; } | ||
430 | struct Foo { bar: Type![] } | ||
258 | 431 | ||
259 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | 432 | impl Foo { |
260 | db.diag(|d| { | 433 | fn new() -> Self { |
261 | // FXIME: macros... | 434 | Foo { bar: 0 } |
262 | let file_id = d.source().file_id.original_file(&db); | 435 | } |
263 | let range = d.syntax_node(&db).text_range(); | 436 | } |
264 | let message = d.message().to_owned(); | 437 | "#, |
265 | actual.entry(file_id).or_default().push((range, message)); | 438 | ); |
266 | }); | 439 | } |
267 | actual.values_mut().for_each(|diags| diags.sort_by_key(|it| it.0.start())); | 440 | |
441 | #[test] | ||
442 | fn missing_record_pat_field_diagnostic() { | ||
443 | check_diagnostics( | ||
444 | r#" | ||
445 | struct S { foo: i32, bar: () } | ||
446 | fn baz(s: S) { | ||
447 | let S { foo: _ } = s; | ||
448 | //^^^^^^^^^^ Missing structure fields: | ||
449 | // | - bar | ||
450 | } | ||
451 | "#, | ||
452 | ); | ||
453 | } | ||
268 | 454 | ||
269 | assert_eq!(annotations, actual); | 455 | #[test] |
456 | fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() { | ||
457 | check_diagnostics( | ||
458 | r" | ||
459 | struct S { foo: i32, bar: () } | ||
460 | fn baz(s: S) -> i32 { | ||
461 | match s { | ||
462 | S { foo, .. } => foo, | ||
463 | } | ||
464 | } | ||
465 | ", | ||
466 | ) | ||
467 | } | ||
468 | |||
469 | #[test] | ||
470 | fn break_outside_of_loop() { | ||
471 | check_diagnostics( | ||
472 | r#" | ||
473 | fn foo() { break; } | ||
474 | //^^^^^ break outside of loop | ||
475 | "#, | ||
476 | ); | ||
477 | } | ||
270 | } | 478 | } |
diff --git a/crates/ra_hir_ty/src/diagnostics/expr.rs b/crates/ra_hir_ty/src/diagnostics/expr.rs index 277ace180..91caedc3d 100644 --- a/crates/ra_hir_ty/src/diagnostics/expr.rs +++ b/crates/ra_hir_ty/src/diagnostics/expr.rs | |||
@@ -376,7 +376,7 @@ pub fn record_pattern_missing_fields( | |||
376 | 376 | ||
377 | #[cfg(test)] | 377 | #[cfg(test)] |
378 | mod tests { | 378 | mod tests { |
379 | use crate::diagnostics::check_diagnostics; | 379 | use crate::diagnostics::tests::check_diagnostics; |
380 | 380 | ||
381 | #[test] | 381 | #[test] |
382 | fn simple_free_fn_zero() { | 382 | fn simple_free_fn_zero() { |
diff --git a/crates/ra_hir_ty/src/diagnostics/match_check.rs b/crates/ra_hir_ty/src/diagnostics/match_check.rs index 95f272811..507edcb7d 100644 --- a/crates/ra_hir_ty/src/diagnostics/match_check.rs +++ b/crates/ra_hir_ty/src/diagnostics/match_check.rs | |||
@@ -837,7 +837,7 @@ fn enum_variant_matches(cx: &MatchCheckCtx, pat_id: PatId, enum_variant_id: Enum | |||
837 | 837 | ||
838 | #[cfg(test)] | 838 | #[cfg(test)] |
839 | mod tests { | 839 | mod tests { |
840 | use crate::diagnostics::check_diagnostics; | 840 | use crate::diagnostics::tests::check_diagnostics; |
841 | 841 | ||
842 | #[test] | 842 | #[test] |
843 | fn empty_tuple() { | 843 | fn empty_tuple() { |
diff --git a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs index b8ff95ee1..9e4ed9a8b 100644 --- a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs +++ b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs | |||
@@ -121,3 +121,53 @@ fn walk_unsafe( | |||
121 | walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block); | 121 | walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block); |
122 | }); | 122 | }); |
123 | } | 123 | } |
124 | |||
125 | #[cfg(test)] | ||
126 | mod tests { | ||
127 | use crate::diagnostics::tests::check_diagnostics; | ||
128 | |||
129 | #[test] | ||
130 | fn missing_unsafe_diagnostic_with_raw_ptr() { | ||
131 | check_diagnostics( | ||
132 | r#" | ||
133 | fn main() { | ||
134 | let x = &5 as *const usize; | ||
135 | unsafe { let y = *x; } | ||
136 | let z = *x; | ||
137 | } //^^ This operation is unsafe and requires an unsafe function or block | ||
138 | "#, | ||
139 | ) | ||
140 | } | ||
141 | |||
142 | #[test] | ||
143 | fn missing_unsafe_diagnostic_with_unsafe_call() { | ||
144 | check_diagnostics( | ||
145 | r#" | ||
146 | struct HasUnsafe; | ||
147 | |||
148 | impl HasUnsafe { | ||
149 | unsafe fn unsafe_fn(&self) { | ||
150 | let x = &5 as *const usize; | ||
151 | let y = *x; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | unsafe fn unsafe_fn() { | ||
156 | let x = &5 as *const usize; | ||
157 | let y = *x; | ||
158 | } | ||
159 | |||
160 | fn main() { | ||
161 | unsafe_fn(); | ||
162 | //^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
163 | HasUnsafe.unsafe_fn(); | ||
164 | //^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
165 | unsafe { | ||
166 | unsafe_fn(); | ||
167 | HasUnsafe.unsafe_fn(); | ||
168 | } | ||
169 | } | ||
170 | "#, | ||
171 | ); | ||
172 | } | ||
173 | } | ||
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index fb8723fb7..a1714ff0f 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs | |||
@@ -5,19 +5,13 @@ use std::{ | |||
5 | sync::{Arc, Mutex}, | 5 | sync::{Arc, Mutex}, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; | 8 | use hir_def::{db::DefDatabase, ModuleId}; |
9 | use hir_expand::{ | 9 | use hir_expand::db::AstDatabase; |
10 | db::AstDatabase, | ||
11 | diagnostics::{Diagnostic, DiagnosticSink}, | ||
12 | }; | ||
13 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; | 10 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; |
14 | use ra_syntax::TextRange; | 11 | use ra_syntax::TextRange; |
15 | use rustc_hash::{FxHashMap, FxHashSet}; | 12 | use rustc_hash::{FxHashMap, FxHashSet}; |
16 | use stdx::format_to; | ||
17 | use test_utils::extract_annotations; | 13 | use test_utils::extract_annotations; |
18 | 14 | ||
19 | use crate::diagnostics::validate_body; | ||
20 | |||
21 | #[salsa::database( | 15 | #[salsa::database( |
22 | ra_db::SourceDatabaseExtStorage, | 16 | ra_db::SourceDatabaseExtStorage, |
23 | ra_db::SourceDatabaseStorage, | 17 | ra_db::SourceDatabaseStorage, |
@@ -94,46 +88,6 @@ impl TestDB { | |||
94 | panic!("Can't find module for file") | 88 | panic!("Can't find module for file") |
95 | } | 89 | } |
96 | 90 | ||
97 | pub(crate) fn diag<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) { | ||
98 | let crate_graph = self.crate_graph(); | ||
99 | for krate in crate_graph.iter() { | ||
100 | let crate_def_map = self.crate_def_map(krate); | ||
101 | |||
102 | let mut fns = Vec::new(); | ||
103 | for (module_id, _) in crate_def_map.modules.iter() { | ||
104 | for decl in crate_def_map[module_id].scope.declarations() { | ||
105 | if let ModuleDefId::FunctionId(f) = decl { | ||
106 | fns.push(f) | ||
107 | } | ||
108 | } | ||
109 | |||
110 | for impl_id in crate_def_map[module_id].scope.impls() { | ||
111 | let impl_data = self.impl_data(impl_id); | ||
112 | for item in impl_data.items.iter() { | ||
113 | if let AssocItemId::FunctionId(f) = item { | ||
114 | fns.push(*f) | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | } | ||
119 | |||
120 | for f in fns { | ||
121 | let mut sink = DiagnosticSink::new(&mut cb); | ||
122 | validate_body(self, f.into(), &mut sink); | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
127 | pub(crate) fn diagnostics(&self) -> (String, u32) { | ||
128 | let mut buf = String::new(); | ||
129 | let mut count = 0; | ||
130 | self.diag(|d| { | ||
131 | format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); | ||
132 | count += 1; | ||
133 | }); | ||
134 | (buf, count) | ||
135 | } | ||
136 | |||
137 | pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> { | 91 | pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> { |
138 | let mut files = Vec::new(); | 92 | let mut files = Vec::new(); |
139 | let crate_graph = self.crate_graph(); | 93 | let crate_graph = self.crate_graph(); |
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index 27f5a60bf..d57b3f288 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs | |||
@@ -20,7 +20,6 @@ use hir_def::{ | |||
20 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, | 20 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, |
21 | }; | 21 | }; |
22 | use hir_expand::{db::AstDatabase, InFile}; | 22 | use hir_expand::{db::AstDatabase, InFile}; |
23 | use insta::assert_snapshot; | ||
24 | use ra_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; | 23 | use ra_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; |
25 | use ra_syntax::{ | 24 | use ra_syntax::{ |
26 | algo, | 25 | algo, |
@@ -341,410 +340,3 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() { | |||
341 | assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) | 340 | assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) |
342 | } | 341 | } |
343 | } | 342 | } |
344 | |||
345 | #[test] | ||
346 | fn no_such_field_diagnostics() { | ||
347 | let diagnostics = TestDB::with_files( | ||
348 | r" | ||
349 | //- /lib.rs | ||
350 | struct S { foo: i32, bar: () } | ||
351 | impl S { | ||
352 | fn new() -> S { | ||
353 | S { | ||
354 | foo: 92, | ||
355 | baz: 62, | ||
356 | } | ||
357 | } | ||
358 | } | ||
359 | ", | ||
360 | ) | ||
361 | .diagnostics() | ||
362 | .0; | ||
363 | |||
364 | assert_snapshot!(diagnostics, @r###" | ||
365 | "baz: 62": no such field | ||
366 | "{\n foo: 92,\n baz: 62,\n }": Missing structure fields: | ||
367 | - bar | ||
368 | "### | ||
369 | ); | ||
370 | } | ||
371 | |||
372 | #[test] | ||
373 | fn no_such_field_with_feature_flag_diagnostics() { | ||
374 | let diagnostics = TestDB::with_files( | ||
375 | r#" | ||
376 | //- /lib.rs crate:foo cfg:feature=foo | ||
377 | struct MyStruct { | ||
378 | my_val: usize, | ||
379 | #[cfg(feature = "foo")] | ||
380 | bar: bool, | ||
381 | } | ||
382 | |||
383 | impl MyStruct { | ||
384 | #[cfg(feature = "foo")] | ||
385 | pub(crate) fn new(my_val: usize, bar: bool) -> Self { | ||
386 | Self { my_val, bar } | ||
387 | } | ||
388 | |||
389 | #[cfg(not(feature = "foo"))] | ||
390 | pub(crate) fn new(my_val: usize, _bar: bool) -> Self { | ||
391 | Self { my_val } | ||
392 | } | ||
393 | } | ||
394 | "#, | ||
395 | ) | ||
396 | .diagnostics() | ||
397 | .0; | ||
398 | |||
399 | assert_snapshot!(diagnostics, @r###""###); | ||
400 | } | ||
401 | |||
402 | #[test] | ||
403 | fn no_such_field_enum_with_feature_flag_diagnostics() { | ||
404 | let diagnostics = TestDB::with_files( | ||
405 | r#" | ||
406 | //- /lib.rs crate:foo cfg:feature=foo | ||
407 | enum Foo { | ||
408 | #[cfg(not(feature = "foo"))] | ||
409 | Buz, | ||
410 | #[cfg(feature = "foo")] | ||
411 | Bar, | ||
412 | Baz | ||
413 | } | ||
414 | |||
415 | fn test_fn(f: Foo) { | ||
416 | match f { | ||
417 | Foo::Bar => {}, | ||
418 | Foo::Baz => {}, | ||
419 | } | ||
420 | } | ||
421 | "#, | ||
422 | ) | ||
423 | .diagnostics() | ||
424 | .0; | ||
425 | |||
426 | assert_snapshot!(diagnostics, @r###""###); | ||
427 | } | ||
428 | |||
429 | #[test] | ||
430 | fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { | ||
431 | let diagnostics = TestDB::with_files( | ||
432 | r#" | ||
433 | //- /lib.rs crate:foo cfg:feature=foo | ||
434 | struct S { | ||
435 | #[cfg(feature = "foo")] | ||
436 | foo: u32, | ||
437 | #[cfg(not(feature = "foo"))] | ||
438 | bar: u32, | ||
439 | } | ||
440 | |||
441 | impl S { | ||
442 | #[cfg(feature = "foo")] | ||
443 | fn new(foo: u32) -> Self { | ||
444 | Self { foo } | ||
445 | } | ||
446 | #[cfg(not(feature = "foo"))] | ||
447 | fn new(bar: u32) -> Self { | ||
448 | Self { bar } | ||
449 | } | ||
450 | } | ||
451 | "#, | ||
452 | ) | ||
453 | .diagnostics() | ||
454 | .0; | ||
455 | |||
456 | assert_snapshot!(diagnostics, @r###""###); | ||
457 | } | ||
458 | |||
459 | #[test] | ||
460 | fn no_such_field_with_feature_flag_diagnostics_on_block_expr() { | ||
461 | let diagnostics = TestDB::with_files( | ||
462 | r#" | ||
463 | //- /lib.rs crate:foo cfg:feature=foo | ||
464 | struct S { | ||
465 | #[cfg(feature = "foo")] | ||
466 | foo: u32, | ||
467 | #[cfg(not(feature = "foo"))] | ||
468 | bar: u32, | ||
469 | } | ||
470 | |||
471 | impl S { | ||
472 | fn new(bar: u32) -> Self { | ||
473 | #[cfg(feature = "foo")] | ||
474 | { | ||
475 | Self { foo: bar } | ||
476 | } | ||
477 | #[cfg(not(feature = "foo"))] | ||
478 | { | ||
479 | Self { bar } | ||
480 | } | ||
481 | } | ||
482 | } | ||
483 | "#, | ||
484 | ) | ||
485 | .diagnostics() | ||
486 | .0; | ||
487 | |||
488 | assert_snapshot!(diagnostics, @r###""###); | ||
489 | } | ||
490 | |||
491 | #[test] | ||
492 | fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() { | ||
493 | let diagnostics = TestDB::with_files( | ||
494 | r#" | ||
495 | //- /lib.rs crate:foo cfg:feature=foo | ||
496 | struct S { | ||
497 | #[cfg(feature = "foo")] | ||
498 | foo: u32, | ||
499 | #[cfg(not(feature = "foo"))] | ||
500 | bar: u32, | ||
501 | } | ||
502 | |||
503 | impl S { | ||
504 | fn new(val: u32) -> Self { | ||
505 | Self { | ||
506 | #[cfg(feature = "foo")] | ||
507 | foo: val, | ||
508 | #[cfg(not(feature = "foo"))] | ||
509 | bar: val, | ||
510 | } | ||
511 | } | ||
512 | } | ||
513 | "#, | ||
514 | ) | ||
515 | .diagnostics() | ||
516 | .0; | ||
517 | |||
518 | assert_snapshot!(diagnostics, @r###""###); | ||
519 | } | ||
520 | |||
521 | #[test] | ||
522 | fn no_such_field_with_type_macro() { | ||
523 | let diagnostics = TestDB::with_files( | ||
524 | r" | ||
525 | macro_rules! Type { | ||
526 | () => { u32 }; | ||
527 | } | ||
528 | |||
529 | struct Foo { | ||
530 | bar: Type![], | ||
531 | } | ||
532 | impl Foo { | ||
533 | fn new() -> Self { | ||
534 | Foo { bar: 0 } | ||
535 | } | ||
536 | } | ||
537 | ", | ||
538 | ) | ||
539 | .diagnostics() | ||
540 | .0; | ||
541 | |||
542 | assert_snapshot!(diagnostics, @r###""###); | ||
543 | } | ||
544 | |||
545 | #[test] | ||
546 | fn missing_record_pat_field_diagnostic() { | ||
547 | let diagnostics = TestDB::with_files( | ||
548 | r" | ||
549 | //- /lib.rs | ||
550 | struct S { foo: i32, bar: () } | ||
551 | fn baz(s: S) { | ||
552 | let S { foo: _ } = s; | ||
553 | } | ||
554 | ", | ||
555 | ) | ||
556 | .diagnostics() | ||
557 | .0; | ||
558 | |||
559 | assert_snapshot!(diagnostics, @r###" | ||
560 | "{ foo: _ }": Missing structure fields: | ||
561 | - bar | ||
562 | "### | ||
563 | ); | ||
564 | } | ||
565 | |||
566 | #[test] | ||
567 | fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() { | ||
568 | let diagnostics = TestDB::with_files( | ||
569 | r" | ||
570 | //- /lib.rs | ||
571 | struct S { foo: i32, bar: () } | ||
572 | fn baz(s: S) -> i32 { | ||
573 | match s { | ||
574 | S { foo, .. } => foo, | ||
575 | } | ||
576 | } | ||
577 | ", | ||
578 | ) | ||
579 | .diagnostics() | ||
580 | .0; | ||
581 | |||
582 | assert_snapshot!(diagnostics, @""); | ||
583 | } | ||
584 | |||
585 | #[test] | ||
586 | fn missing_unsafe_diagnostic_with_raw_ptr() { | ||
587 | let diagnostics = TestDB::with_files( | ||
588 | r" | ||
589 | //- /lib.rs | ||
590 | fn missing_unsafe() { | ||
591 | let x = &5 as *const usize; | ||
592 | let y = *x; | ||
593 | } | ||
594 | ", | ||
595 | ) | ||
596 | .diagnostics() | ||
597 | .0; | ||
598 | |||
599 | assert_snapshot!(diagnostics, @r#""*x": This operation is unsafe and requires an unsafe function or block"#); | ||
600 | } | ||
601 | |||
602 | #[test] | ||
603 | fn missing_unsafe_diagnostic_with_unsafe_call() { | ||
604 | let diagnostics = TestDB::with_files( | ||
605 | r" | ||
606 | //- /lib.rs | ||
607 | unsafe fn unsafe_fn() { | ||
608 | let x = &5 as *const usize; | ||
609 | let y = *x; | ||
610 | } | ||
611 | |||
612 | fn missing_unsafe() { | ||
613 | unsafe_fn(); | ||
614 | } | ||
615 | ", | ||
616 | ) | ||
617 | .diagnostics() | ||
618 | .0; | ||
619 | |||
620 | assert_snapshot!(diagnostics, @r#""unsafe_fn()": This operation is unsafe and requires an unsafe function or block"#); | ||
621 | } | ||
622 | |||
623 | #[test] | ||
624 | fn missing_unsafe_diagnostic_with_unsafe_method_call() { | ||
625 | let diagnostics = TestDB::with_files( | ||
626 | r" | ||
627 | struct HasUnsafe; | ||
628 | |||
629 | impl HasUnsafe { | ||
630 | unsafe fn unsafe_fn(&self) { | ||
631 | let x = &5 as *const usize; | ||
632 | let y = *x; | ||
633 | } | ||
634 | } | ||
635 | |||
636 | fn missing_unsafe() { | ||
637 | HasUnsafe.unsafe_fn(); | ||
638 | } | ||
639 | |||
640 | ", | ||
641 | ) | ||
642 | .diagnostics() | ||
643 | .0; | ||
644 | |||
645 | assert_snapshot!(diagnostics, @r#""HasUnsafe.unsafe_fn()": This operation is unsafe and requires an unsafe function or block"#); | ||
646 | } | ||
647 | |||
648 | #[test] | ||
649 | fn no_missing_unsafe_diagnostic_with_raw_ptr_in_unsafe_block() { | ||
650 | let diagnostics = TestDB::with_files( | ||
651 | r" | ||
652 | fn nothing_to_see_move_along() { | ||
653 | let x = &5 as *const usize; | ||
654 | unsafe { | ||
655 | let y = *x; | ||
656 | } | ||
657 | } | ||
658 | ", | ||
659 | ) | ||
660 | .diagnostics() | ||
661 | .0; | ||
662 | |||
663 | assert_snapshot!(diagnostics, @""); | ||
664 | } | ||
665 | |||
666 | #[test] | ||
667 | fn missing_unsafe_diagnostic_with_raw_ptr_outside_unsafe_block() { | ||
668 | let diagnostics = TestDB::with_files( | ||
669 | r" | ||
670 | fn nothing_to_see_move_along() { | ||
671 | let x = &5 as *const usize; | ||
672 | unsafe { | ||
673 | let y = *x; | ||
674 | } | ||
675 | let z = *x; | ||
676 | } | ||
677 | ", | ||
678 | ) | ||
679 | .diagnostics() | ||
680 | .0; | ||
681 | |||
682 | assert_snapshot!(diagnostics, @r#""*x": This operation is unsafe and requires an unsafe function or block"#); | ||
683 | } | ||
684 | |||
685 | #[test] | ||
686 | fn no_missing_unsafe_diagnostic_with_unsafe_call_in_unsafe_block() { | ||
687 | let diagnostics = TestDB::with_files( | ||
688 | r" | ||
689 | unsafe fn unsafe_fn() { | ||
690 | let x = &5 as *const usize; | ||
691 | let y = *x; | ||
692 | } | ||
693 | |||
694 | fn nothing_to_see_move_along() { | ||
695 | unsafe { | ||
696 | unsafe_fn(); | ||
697 | } | ||
698 | } | ||
699 | ", | ||
700 | ) | ||
701 | .diagnostics() | ||
702 | .0; | ||
703 | |||
704 | assert_snapshot!(diagnostics, @""); | ||
705 | } | ||
706 | |||
707 | #[test] | ||
708 | fn no_missing_unsafe_diagnostic_with_unsafe_method_call_in_unsafe_block() { | ||
709 | let diagnostics = TestDB::with_files( | ||
710 | r" | ||
711 | struct HasUnsafe; | ||
712 | |||
713 | impl HasUnsafe { | ||
714 | unsafe fn unsafe_fn() { | ||
715 | let x = &5 as *const usize; | ||
716 | let y = *x; | ||
717 | } | ||
718 | } | ||
719 | |||
720 | fn nothing_to_see_move_along() { | ||
721 | unsafe { | ||
722 | HasUnsafe.unsafe_fn(); | ||
723 | } | ||
724 | } | ||
725 | |||
726 | ", | ||
727 | ) | ||
728 | .diagnostics() | ||
729 | .0; | ||
730 | |||
731 | assert_snapshot!(diagnostics, @""); | ||
732 | } | ||
733 | |||
734 | #[test] | ||
735 | fn break_outside_of_loop() { | ||
736 | let diagnostics = TestDB::with_files( | ||
737 | r" | ||
738 | //- /lib.rs | ||
739 | fn foo() { | ||
740 | break; | ||
741 | } | ||
742 | ", | ||
743 | ) | ||
744 | .diagnostics() | ||
745 | .0; | ||
746 | |||
747 | assert_snapshot!(diagnostics, @r###""break": break outside of loop | ||
748 | "### | ||
749 | ); | ||
750 | } | ||