diff options
Diffstat (limited to 'crates/ra_hir_ty')
-rw-r--r-- | crates/ra_hir_ty/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics.rs | 253 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/expr.rs (renamed from crates/ra_hir_ty/src/expr.rs) | 252 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/match_check.rs (renamed from crates/ra_hir_ty/src/match_checking.rs) | 1678 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/unsafe_check.rs (renamed from crates/ra_hir_ty/src/unsafe_validation.rs) | 73 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/display.rs | 11 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer.rs | 10 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer/expr.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lib.rs | 22 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lower.rs | 13 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/test_db.rs | 74 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests.rs | 408 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/traits/builtin.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/traits/chalk/mapping.rs | 8 |
14 files changed, 969 insertions, 1838 deletions
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 90368220b..78f5e55bb 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_hir_ty" | 3 | name = "ra_hir_ty" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index 5b0dda634..d3ee9cf55 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs | |||
@@ -1,13 +1,30 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | mod expr; | ||
3 | mod match_check; | ||
4 | mod unsafe_check; | ||
2 | 5 | ||
3 | use std::any::Any; | 6 | use std::any::Any; |
4 | 7 | ||
8 | use hir_def::DefWithBodyId; | ||
9 | use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; | ||
5 | use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; | 10 | use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; |
11 | use ra_prof::profile; | ||
6 | use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; | 12 | use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; |
7 | use stdx::format_to; | 13 | use stdx::format_to; |
8 | 14 | ||
9 | pub use hir_def::{diagnostics::UnresolvedModule, expr::MatchArm, path::Path}; | 15 | use crate::db::HirDatabase; |
10 | pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; | 16 | |
17 | pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields}; | ||
18 | |||
19 | pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { | ||
20 | let _p = profile("validate_body"); | ||
21 | let infer = db.infer(owner); | ||
22 | infer.add_diagnostics(db, owner, sink); | ||
23 | let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink); | ||
24 | validator.validate_body(db); | ||
25 | let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink); | ||
26 | validator.validate_body(db); | ||
27 | } | ||
11 | 28 | ||
12 | #[derive(Debug)] | 29 | #[derive(Debug)] |
13 | pub struct NoSuchField { | 30 | pub struct NoSuchField { |
@@ -227,3 +244,235 @@ impl AstDiagnostic for MismatchedArgCount { | |||
227 | ast::CallExpr::cast(node).unwrap() | 244 | ast::CallExpr::cast(node).unwrap() |
228 | } | 245 | } |
229 | } | 246 | } |
247 | |||
248 | #[cfg(test)] | ||
249 | mod tests { | ||
250 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; | ||
251 | use hir_expand::diagnostics::{Diagnostic, DiagnosticSink}; | ||
252 | use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; | ||
253 | use ra_syntax::{TextRange, TextSize}; | ||
254 | use rustc_hash::FxHashMap; | ||
255 | |||
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 | } | ||
424 | |||
425 | #[test] | ||
426 | fn no_such_field_with_type_macro() { | ||
427 | check_diagnostics( | ||
428 | r#" | ||
429 | macro_rules! Type { () => { u32 }; } | ||
430 | struct Foo { bar: Type![] } | ||
431 | |||
432 | impl Foo { | ||
433 | fn new() -> Self { | ||
434 | Foo { bar: 0 } | ||
435 | } | ||
436 | } | ||
437 | "#, | ||
438 | ); | ||
439 | } | ||
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 | } | ||
454 | |||
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 | } | ||
478 | } | ||
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/diagnostics/expr.rs index d44562b22..557d01cdc 100644 --- a/crates/ra_hir_ty/src/expr.rs +++ b/crates/ra_hir_ty/src/diagnostics/expr.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId}; | 5 | use hir_def::{path::path, resolver::HasResolver, AdtId, DefWithBodyId}; |
6 | use hir_expand::diagnostics::DiagnosticSink; | 6 | use hir_expand::diagnostics::DiagnosticSink; |
7 | use ra_syntax::{ast, AstPtr}; | 7 | use ra_syntax::{ast, AstPtr}; |
8 | use rustc_hash::FxHashSet; | 8 | use rustc_hash::FxHashSet; |
@@ -10,9 +10,9 @@ use rustc_hash::FxHashSet; | |||
10 | use crate::{ | 10 | use crate::{ |
11 | db::HirDatabase, | 11 | db::HirDatabase, |
12 | diagnostics::{ | 12 | diagnostics::{ |
13 | match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, | ||
13 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields, | 14 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields, |
14 | }, | 15 | }, |
15 | match_checking::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, | ||
16 | utils::variant_data, | 16 | utils::variant_data, |
17 | ApplicationTy, InferenceResult, Ty, TypeCtor, | 17 | ApplicationTy, InferenceResult, Ty, TypeCtor, |
18 | }; | 18 | }; |
@@ -30,23 +30,23 @@ pub use hir_def::{ | |||
30 | LocalFieldId, Lookup, VariantId, | 30 | LocalFieldId, Lookup, VariantId, |
31 | }; | 31 | }; |
32 | 32 | ||
33 | pub struct ExprValidator<'a, 'b: 'a> { | 33 | pub(super) struct ExprValidator<'a, 'b: 'a> { |
34 | func: FunctionId, | 34 | owner: DefWithBodyId, |
35 | infer: Arc<InferenceResult>, | 35 | infer: Arc<InferenceResult>, |
36 | sink: &'a mut DiagnosticSink<'b>, | 36 | sink: &'a mut DiagnosticSink<'b>, |
37 | } | 37 | } |
38 | 38 | ||
39 | impl<'a, 'b> ExprValidator<'a, 'b> { | 39 | impl<'a, 'b> ExprValidator<'a, 'b> { |
40 | pub fn new( | 40 | pub(super) fn new( |
41 | func: FunctionId, | 41 | owner: DefWithBodyId, |
42 | infer: Arc<InferenceResult>, | 42 | infer: Arc<InferenceResult>, |
43 | sink: &'a mut DiagnosticSink<'b>, | 43 | sink: &'a mut DiagnosticSink<'b>, |
44 | ) -> ExprValidator<'a, 'b> { | 44 | ) -> ExprValidator<'a, 'b> { |
45 | ExprValidator { func, infer, sink } | 45 | ExprValidator { owner, infer, sink } |
46 | } | 46 | } |
47 | 47 | ||
48 | pub fn validate_body(&mut self, db: &dyn HirDatabase) { | 48 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { |
49 | let body = db.body(self.func.into()); | 49 | let body = db.body(self.owner.into()); |
50 | 50 | ||
51 | for (id, expr) in body.exprs.iter() { | 51 | for (id, expr) in body.exprs.iter() { |
52 | if let Some((variant_def, missed_fields, true)) = | 52 | if let Some((variant_def, missed_fields, true)) = |
@@ -96,7 +96,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
96 | missed_fields: Vec<LocalFieldId>, | 96 | missed_fields: Vec<LocalFieldId>, |
97 | ) { | 97 | ) { |
98 | // XXX: only look at source_map if we do have missing fields | 98 | // XXX: only look at source_map if we do have missing fields |
99 | let (_, source_map) = db.body_with_source_map(self.func.into()); | 99 | let (_, source_map) = db.body_with_source_map(self.owner.into()); |
100 | 100 | ||
101 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | 101 | if let Ok(source_ptr) = source_map.expr_syntax(id) { |
102 | let root = source_ptr.file_syntax(db.upcast()); | 102 | let root = source_ptr.file_syntax(db.upcast()); |
@@ -125,7 +125,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
125 | missed_fields: Vec<LocalFieldId>, | 125 | missed_fields: Vec<LocalFieldId>, |
126 | ) { | 126 | ) { |
127 | // XXX: only look at source_map if we do have missing fields | 127 | // XXX: only look at source_map if we do have missing fields |
128 | let (_, source_map) = db.body_with_source_map(self.func.into()); | 128 | let (_, source_map) = db.body_with_source_map(self.owner.into()); |
129 | 129 | ||
130 | if let Ok(source_ptr) = source_map.pat_syntax(id) { | 130 | if let Ok(source_ptr) = source_map.pat_syntax(id) { |
131 | if let Some(expr) = source_ptr.value.as_ref().left() { | 131 | if let Some(expr) = source_ptr.value.as_ref().left() { |
@@ -175,13 +175,17 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
175 | }; | 175 | }; |
176 | 176 | ||
177 | let sig = db.callable_item_signature(callee); | 177 | let sig = db.callable_item_signature(callee); |
178 | if sig.value.is_varargs { | ||
179 | return None; | ||
180 | } | ||
181 | |||
178 | let params = sig.value.params(); | 182 | let params = sig.value.params(); |
179 | 183 | ||
180 | let mut param_count = params.len(); | 184 | let mut param_count = params.len(); |
181 | let mut arg_count = args.len(); | 185 | let mut arg_count = args.len(); |
182 | 186 | ||
183 | if arg_count != param_count { | 187 | if arg_count != param_count { |
184 | let (_, source_map) = db.body_with_source_map(self.func.into()); | 188 | let (_, source_map) = db.body_with_source_map(self.owner.into()); |
185 | if let Ok(source_ptr) = source_map.expr_syntax(call_id) { | 189 | if let Ok(source_ptr) = source_map.expr_syntax(call_id) { |
186 | if is_method_call { | 190 | if is_method_call { |
187 | param_count -= 1; | 191 | param_count -= 1; |
@@ -208,7 +212,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
208 | infer: Arc<InferenceResult>, | 212 | infer: Arc<InferenceResult>, |
209 | ) { | 213 | ) { |
210 | let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) = | 214 | let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) = |
211 | db.body_with_source_map(self.func.into()); | 215 | db.body_with_source_map(self.owner.into()); |
212 | 216 | ||
213 | let match_expr_ty = match infer.type_of_expr.get(match_expr) { | 217 | let match_expr_ty = match infer.type_of_expr.get(match_expr) { |
214 | Some(ty) => ty, | 218 | Some(ty) => ty, |
@@ -289,7 +293,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
289 | 293 | ||
290 | let core_result_path = path![core::result::Result]; | 294 | let core_result_path = path![core::result::Result]; |
291 | 295 | ||
292 | let resolver = self.func.resolver(db.upcast()); | 296 | let resolver = self.owner.resolver(db.upcast()); |
293 | let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) { | 297 | let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) { |
294 | Some(it) => it, | 298 | Some(it) => it, |
295 | _ => return, | 299 | _ => return, |
@@ -304,7 +308,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
304 | }; | 308 | }; |
305 | 309 | ||
306 | if params.len() == 2 && params[0] == mismatch.actual { | 310 | if params.len() == 2 && params[0] == mismatch.actual { |
307 | let (_, source_map) = db.body_with_source_map(self.func.into()); | 311 | let (_, source_map) = db.body_with_source_map(self.owner.into()); |
308 | 312 | ||
309 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | 313 | if let Ok(source_ptr) = source_map.expr_syntax(id) { |
310 | self.sink | 314 | self.sink |
@@ -376,146 +380,166 @@ pub fn record_pattern_missing_fields( | |||
376 | 380 | ||
377 | #[cfg(test)] | 381 | #[cfg(test)] |
378 | mod tests { | 382 | mod tests { |
379 | use expect::{expect, Expect}; | 383 | use crate::diagnostics::tests::check_diagnostics; |
380 | use ra_db::fixture::WithFixture; | ||
381 | |||
382 | use crate::{diagnostics::MismatchedArgCount, test_db::TestDB}; | ||
383 | |||
384 | fn check_diagnostic(ra_fixture: &str, expect: Expect) { | ||
385 | let msg = TestDB::with_single_file(ra_fixture).0.diagnostic::<MismatchedArgCount>().0; | ||
386 | expect.assert_eq(&msg); | ||
387 | } | ||
388 | |||
389 | fn check_no_diagnostic(ra_fixture: &str) { | ||
390 | let (s, diagnostic_count) = | ||
391 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MismatchedArgCount>(); | ||
392 | |||
393 | assert_eq!(0, diagnostic_count, "expected no diagnostic, found one: {}", s); | ||
394 | } | ||
395 | 384 | ||
396 | #[test] | 385 | #[test] |
397 | fn simple_free_fn_zero() { | 386 | fn simple_free_fn_zero() { |
398 | check_diagnostic( | 387 | check_diagnostics( |
399 | r" | 388 | r#" |
400 | fn zero() {} | 389 | fn zero() {} |
401 | fn f() { zero(1); } | 390 | fn f() { zero(1); } |
402 | ", | 391 | //^^^^^^^ Expected 0 arguments, found 1 |
403 | expect![["\"zero(1)\": Expected 0 arguments, found 1\n"]], | 392 | "#, |
404 | ); | 393 | ); |
405 | 394 | ||
406 | check_no_diagnostic( | 395 | check_diagnostics( |
407 | r" | 396 | r#" |
408 | fn zero() {} | 397 | fn zero() {} |
409 | fn f() { zero(); } | 398 | fn f() { zero(); } |
410 | ", | 399 | "#, |
411 | ); | 400 | ); |
412 | } | 401 | } |
413 | 402 | ||
414 | #[test] | 403 | #[test] |
415 | fn simple_free_fn_one() { | 404 | fn simple_free_fn_one() { |
416 | check_diagnostic( | 405 | check_diagnostics( |
417 | r" | 406 | r#" |
418 | fn one(arg: u8) {} | 407 | fn one(arg: u8) {} |
419 | fn f() { one(); } | 408 | fn f() { one(); } |
420 | ", | 409 | //^^^^^ Expected 1 argument, found 0 |
421 | expect![["\"one()\": Expected 1 argument, found 0\n"]], | 410 | "#, |
422 | ); | 411 | ); |
423 | 412 | ||
424 | check_no_diagnostic( | 413 | check_diagnostics( |
425 | r" | 414 | r#" |
426 | fn one(arg: u8) {} | 415 | fn one(arg: u8) {} |
427 | fn f() { one(1); } | 416 | fn f() { one(1); } |
428 | ", | 417 | "#, |
429 | ); | 418 | ); |
430 | } | 419 | } |
431 | 420 | ||
432 | #[test] | 421 | #[test] |
433 | fn method_as_fn() { | 422 | fn method_as_fn() { |
434 | check_diagnostic( | 423 | check_diagnostics( |
435 | r" | 424 | r#" |
436 | struct S; | 425 | struct S; |
437 | impl S { | 426 | impl S { fn method(&self) {} } |
438 | fn method(&self) {} | 427 | |
439 | } | 428 | fn f() { |
440 | 429 | S::method(); | |
441 | fn f() { | 430 | } //^^^^^^^^^^^ Expected 1 argument, found 0 |
442 | S::method(); | 431 | "#, |
443 | } | ||
444 | ", | ||
445 | expect![["\"S::method()\": Expected 1 argument, found 0\n"]], | ||
446 | ); | 432 | ); |
447 | 433 | ||
448 | check_no_diagnostic( | 434 | check_diagnostics( |
449 | r" | 435 | r#" |
450 | struct S; | 436 | struct S; |
451 | impl S { | 437 | impl S { fn method(&self) {} } |
452 | fn method(&self) {} | ||
453 | } | ||
454 | 438 | ||
455 | fn f() { | 439 | fn f() { |
456 | S::method(&S); | 440 | S::method(&S); |
457 | S.method(); | 441 | S.method(); |
458 | } | 442 | } |
459 | ", | 443 | "#, |
460 | ); | 444 | ); |
461 | } | 445 | } |
462 | 446 | ||
463 | #[test] | 447 | #[test] |
464 | fn method_with_arg() { | 448 | fn method_with_arg() { |
465 | check_diagnostic( | 449 | check_diagnostics( |
466 | r" | 450 | r#" |
467 | struct S; | 451 | struct S; |
468 | impl S { | 452 | impl S { fn method(&self, arg: u8) {} } |
469 | fn method(&self, arg: u8) {} | ||
470 | } | ||
471 | 453 | ||
472 | fn f() { | 454 | fn f() { |
473 | S.method(); | 455 | S.method(); |
474 | } | 456 | } //^^^^^^^^^^ Expected 1 argument, found 0 |
475 | ", | 457 | "#, |
476 | expect![["\"S.method()\": Expected 1 argument, found 0\n"]], | ||
477 | ); | 458 | ); |
478 | 459 | ||
479 | check_no_diagnostic( | 460 | check_diagnostics( |
480 | r" | 461 | r#" |
481 | struct S; | 462 | struct S; |
482 | impl S { | 463 | impl S { fn method(&self, arg: u8) {} } |
483 | fn method(&self, arg: u8) {} | ||
484 | } | ||
485 | 464 | ||
486 | fn f() { | 465 | fn f() { |
487 | S::method(&S, 0); | 466 | S::method(&S, 0); |
488 | S.method(1); | 467 | S.method(1); |
489 | } | 468 | } |
490 | ", | 469 | "#, |
491 | ); | 470 | ); |
492 | } | 471 | } |
493 | 472 | ||
494 | #[test] | 473 | #[test] |
495 | fn tuple_struct() { | 474 | fn tuple_struct() { |
496 | check_diagnostic( | 475 | check_diagnostics( |
497 | r" | 476 | r#" |
498 | struct Tup(u8, u16); | 477 | struct Tup(u8, u16); |
499 | fn f() { | 478 | fn f() { |
500 | Tup(0); | 479 | Tup(0); |
501 | } | 480 | } //^^^^^^ Expected 2 arguments, found 1 |
502 | ", | 481 | "#, |
503 | expect![["\"Tup(0)\": Expected 2 arguments, found 1\n"]], | ||
504 | ) | 482 | ) |
505 | } | 483 | } |
506 | 484 | ||
507 | #[test] | 485 | #[test] |
508 | fn enum_variant() { | 486 | fn enum_variant() { |
509 | check_diagnostic( | 487 | check_diagnostics( |
510 | r" | 488 | r#" |
511 | enum En { | 489 | enum En { Variant(u8, u16), } |
512 | Variant(u8, u16), | 490 | fn f() { |
513 | } | 491 | En::Variant(0); |
514 | fn f() { | 492 | } //^^^^^^^^^^^^^^ Expected 2 arguments, found 1 |
515 | En::Variant(0); | 493 | "#, |
516 | } | 494 | ) |
517 | ", | 495 | } |
518 | expect![["\"En::Variant(0)\": Expected 2 arguments, found 1\n"]], | 496 | |
497 | #[test] | ||
498 | fn enum_variant_type_macro() { | ||
499 | check_diagnostics( | ||
500 | r#" | ||
501 | macro_rules! Type { | ||
502 | () => { u32 }; | ||
503 | } | ||
504 | enum Foo { | ||
505 | Bar(Type![]) | ||
506 | } | ||
507 | impl Foo { | ||
508 | fn new() { | ||
509 | Foo::Bar(0); | ||
510 | Foo::Bar(0, 1); | ||
511 | //^^^^^^^^^^^^^^ Expected 1 argument, found 2 | ||
512 | Foo::Bar(); | ||
513 | //^^^^^^^^^^ Expected 1 argument, found 0 | ||
514 | } | ||
515 | } | ||
516 | "#, | ||
517 | ); | ||
518 | } | ||
519 | |||
520 | #[test] | ||
521 | fn varargs() { | ||
522 | check_diagnostics( | ||
523 | r#" | ||
524 | extern "C" { | ||
525 | fn fixed(fixed: u8); | ||
526 | fn varargs(fixed: u8, ...); | ||
527 | fn varargs2(...); | ||
528 | } | ||
529 | |||
530 | fn f() { | ||
531 | unsafe { | ||
532 | fixed(0); | ||
533 | fixed(0, 1); | ||
534 | //^^^^^^^^^^^ Expected 1 argument, found 2 | ||
535 | varargs(0); | ||
536 | varargs(0, 1); | ||
537 | varargs2(); | ||
538 | varargs2(0); | ||
539 | varargs2(0, 1); | ||
540 | } | ||
541 | } | ||
542 | "#, | ||
519 | ) | 543 | ) |
520 | } | 544 | } |
521 | } | 545 | } |
diff --git a/crates/ra_hir_ty/src/match_checking.rs b/crates/ra_hir_ty/src/diagnostics/match_check.rs index 5495ce284..507edcb7d 100644 --- a/crates/ra_hir_ty/src/match_checking.rs +++ b/crates/ra_hir_ty/src/diagnostics/match_check.rs | |||
@@ -41,9 +41,9 @@ | |||
41 | //! ```ignore | 41 | //! ```ignore |
42 | //! // x: (Option<bool>, Result<()>) | 42 | //! // x: (Option<bool>, Result<()>) |
43 | //! match x { | 43 | //! match x { |
44 | //! (Some(true), _) => {} | 44 | //! (Some(true), _) => (), |
45 | //! (None, Err(())) => {} | 45 | //! (None, Err(())) => (), |
46 | //! (None, Err(_)) => {} | 46 | //! (None, Err(_)) => (), |
47 | //! } | 47 | //! } |
48 | //! ``` | 48 | //! ``` |
49 | //! | 49 | //! |
@@ -218,15 +218,16 @@ | |||
218 | //! ``` | 218 | //! ``` |
219 | use std::sync::Arc; | 219 | use std::sync::Arc; |
220 | 220 | ||
221 | use smallvec::{smallvec, SmallVec}; | 221 | use hir_def::{ |
222 | 222 | adt::VariantData, | |
223 | use crate::{ | 223 | body::Body, |
224 | db::HirDatabase, | 224 | expr::{Expr, Literal, Pat, PatId}, |
225 | expr::{Body, Expr, Literal, Pat, PatId}, | 225 | AdtId, EnumVariantId, VariantId, |
226 | ApplicationTy, InferenceResult, Ty, TypeCtor, | ||
227 | }; | 226 | }; |
228 | use hir_def::{adt::VariantData, AdtId, EnumVariantId, VariantId}; | ||
229 | use ra_arena::Idx; | 227 | use ra_arena::Idx; |
228 | use smallvec::{smallvec, SmallVec}; | ||
229 | |||
230 | use crate::{db::HirDatabase, ApplicationTy, InferenceResult, Ty, TypeCtor}; | ||
230 | 231 | ||
231 | #[derive(Debug, Clone, Copy)] | 232 | #[derive(Debug, Clone, Copy)] |
232 | /// Either a pattern from the source code being analyzed, represented as | 233 | /// Either a pattern from the source code being analyzed, represented as |
@@ -271,7 +272,7 @@ impl From<&PatId> for PatIdOrWild { | |||
271 | } | 272 | } |
272 | 273 | ||
273 | #[derive(Debug, Clone, Copy, PartialEq)] | 274 | #[derive(Debug, Clone, Copy, PartialEq)] |
274 | pub enum MatchCheckErr { | 275 | pub(super) enum MatchCheckErr { |
275 | NotImplemented, | 276 | NotImplemented, |
276 | MalformedMatchArm, | 277 | MalformedMatchArm, |
277 | /// Used when type inference cannot resolve the type of | 278 | /// Used when type inference cannot resolve the type of |
@@ -286,21 +287,21 @@ pub enum MatchCheckErr { | |||
286 | /// | 287 | /// |
287 | /// The `std::result::Result` type is used here rather than a custom enum | 288 | /// The `std::result::Result` type is used here rather than a custom enum |
288 | /// to allow the use of `?`. | 289 | /// to allow the use of `?`. |
289 | pub type MatchCheckResult<T> = Result<T, MatchCheckErr>; | 290 | pub(super) type MatchCheckResult<T> = Result<T, MatchCheckErr>; |
290 | 291 | ||
291 | #[derive(Debug)] | 292 | #[derive(Debug)] |
292 | /// A row in a Matrix. | 293 | /// A row in a Matrix. |
293 | /// | 294 | /// |
294 | /// This type is modeled from the struct of the same name in `rustc`. | 295 | /// This type is modeled from the struct of the same name in `rustc`. |
295 | pub(crate) struct PatStack(PatStackInner); | 296 | pub(super) struct PatStack(PatStackInner); |
296 | type PatStackInner = SmallVec<[PatIdOrWild; 2]>; | 297 | type PatStackInner = SmallVec<[PatIdOrWild; 2]>; |
297 | 298 | ||
298 | impl PatStack { | 299 | impl PatStack { |
299 | pub(crate) fn from_pattern(pat_id: PatId) -> PatStack { | 300 | pub(super) fn from_pattern(pat_id: PatId) -> PatStack { |
300 | Self(smallvec!(pat_id.into())) | 301 | Self(smallvec!(pat_id.into())) |
301 | } | 302 | } |
302 | 303 | ||
303 | pub(crate) fn from_wild() -> PatStack { | 304 | pub(super) fn from_wild() -> PatStack { |
304 | Self(smallvec!(PatIdOrWild::Wild)) | 305 | Self(smallvec!(PatIdOrWild::Wild)) |
305 | } | 306 | } |
306 | 307 | ||
@@ -509,14 +510,14 @@ impl PatStack { | |||
509 | /// A collection of PatStack. | 510 | /// A collection of PatStack. |
510 | /// | 511 | /// |
511 | /// This type is modeled from the struct of the same name in `rustc`. | 512 | /// This type is modeled from the struct of the same name in `rustc`. |
512 | pub(crate) struct Matrix(Vec<PatStack>); | 513 | pub(super) struct Matrix(Vec<PatStack>); |
513 | 514 | ||
514 | impl Matrix { | 515 | impl Matrix { |
515 | pub(crate) fn empty() -> Self { | 516 | pub(super) fn empty() -> Self { |
516 | Self(vec![]) | 517 | Self(vec![]) |
517 | } | 518 | } |
518 | 519 | ||
519 | pub(crate) fn push(&mut self, cx: &MatchCheckCtx, row: PatStack) { | 520 | pub(super) fn push(&mut self, cx: &MatchCheckCtx, row: PatStack) { |
520 | if let Some(Pat::Or(pat_ids)) = row.get_head().map(|pat_id| pat_id.as_pat(cx)) { | 521 | if let Some(Pat::Or(pat_ids)) = row.get_head().map(|pat_id| pat_id.as_pat(cx)) { |
521 | // Or patterns are expanded here | 522 | // Or patterns are expanded here |
522 | for pat_id in pat_ids { | 523 | for pat_id in pat_ids { |
@@ -578,16 +579,16 @@ impl Matrix { | |||
578 | /// not matched by an prior match arms. | 579 | /// not matched by an prior match arms. |
579 | /// | 580 | /// |
580 | /// We may eventually need an `Unknown` variant here. | 581 | /// We may eventually need an `Unknown` variant here. |
581 | pub enum Usefulness { | 582 | pub(super) enum Usefulness { |
582 | Useful, | 583 | Useful, |
583 | NotUseful, | 584 | NotUseful, |
584 | } | 585 | } |
585 | 586 | ||
586 | pub struct MatchCheckCtx<'a> { | 587 | pub(super) struct MatchCheckCtx<'a> { |
587 | pub match_expr: Idx<Expr>, | 588 | pub(super) match_expr: Idx<Expr>, |
588 | pub body: Arc<Body>, | 589 | pub(super) body: Arc<Body>, |
589 | pub infer: Arc<InferenceResult>, | 590 | pub(super) infer: Arc<InferenceResult>, |
590 | pub db: &'a dyn HirDatabase, | 591 | pub(super) db: &'a dyn HirDatabase, |
591 | } | 592 | } |
592 | 593 | ||
593 | /// Given a set of patterns `matrix`, and pattern to consider `v`, determines | 594 | /// Given a set of patterns `matrix`, and pattern to consider `v`, determines |
@@ -598,7 +599,7 @@ pub struct MatchCheckCtx<'a> { | |||
598 | /// expected that you have already type checked the match arms. All patterns in | 599 | /// expected that you have already type checked the match arms. All patterns in |
599 | /// matrix should be the same type as v, as well as they should all be the same | 600 | /// matrix should be the same type as v, as well as they should all be the same |
600 | /// type as the match expression. | 601 | /// type as the match expression. |
601 | pub(crate) fn is_useful( | 602 | pub(super) fn is_useful( |
602 | cx: &MatchCheckCtx, | 603 | cx: &MatchCheckCtx, |
603 | matrix: &Matrix, | 604 | matrix: &Matrix, |
604 | v: &PatStack, | 605 | v: &PatStack, |
@@ -836,685 +837,251 @@ fn enum_variant_matches(cx: &MatchCheckCtx, pat_id: PatId, enum_variant_id: Enum | |||
836 | 837 | ||
837 | #[cfg(test)] | 838 | #[cfg(test)] |
838 | mod tests { | 839 | mod tests { |
839 | pub(super) use insta::assert_snapshot; | 840 | use crate::diagnostics::tests::check_diagnostics; |
840 | pub(super) use ra_db::fixture::WithFixture; | ||
841 | |||
842 | pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB}; | ||
843 | |||
844 | pub(super) fn check_diagnostic_message(ra_fixture: &str) -> String { | ||
845 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().0 | ||
846 | } | ||
847 | |||
848 | pub(super) fn check_diagnostic(ra_fixture: &str) { | ||
849 | let diagnostic_count = | ||
850 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1; | ||
851 | |||
852 | assert_eq!(1, diagnostic_count, "no diagnostic reported"); | ||
853 | } | ||
854 | |||
855 | pub(super) fn check_no_diagnostic(ra_fixture: &str) { | ||
856 | let (s, diagnostic_count) = | ||
857 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>(); | ||
858 | |||
859 | assert_eq!(0, diagnostic_count, "expected no diagnostic, found one: {}", s); | ||
860 | } | ||
861 | |||
862 | #[test] | ||
863 | fn empty_tuple_no_arms_diagnostic_message() { | ||
864 | assert_snapshot!( | ||
865 | check_diagnostic_message(r" | ||
866 | fn test_fn() { | ||
867 | match () { | ||
868 | } | ||
869 | } | ||
870 | "), | ||
871 | @"\"()\": Missing match arm\n" | ||
872 | ); | ||
873 | } | ||
874 | |||
875 | #[test] | ||
876 | fn empty_tuple_no_arms() { | ||
877 | check_diagnostic( | ||
878 | r" | ||
879 | fn test_fn() { | ||
880 | match () { | ||
881 | } | ||
882 | } | ||
883 | ", | ||
884 | ); | ||
885 | } | ||
886 | |||
887 | #[test] | ||
888 | fn empty_tuple_wild() { | ||
889 | check_no_diagnostic( | ||
890 | r" | ||
891 | fn test_fn() { | ||
892 | match () { | ||
893 | _ => {} | ||
894 | } | ||
895 | } | ||
896 | ", | ||
897 | ); | ||
898 | } | ||
899 | |||
900 | #[test] | ||
901 | fn empty_tuple_no_diagnostic() { | ||
902 | check_no_diagnostic( | ||
903 | r" | ||
904 | fn test_fn() { | ||
905 | match () { | ||
906 | () => {} | ||
907 | } | ||
908 | } | ||
909 | ", | ||
910 | ); | ||
911 | } | ||
912 | |||
913 | #[test] | ||
914 | fn tuple_of_empty_tuple_no_arms() { | ||
915 | check_diagnostic( | ||
916 | r" | ||
917 | fn test_fn() { | ||
918 | match (()) { | ||
919 | } | ||
920 | } | ||
921 | ", | ||
922 | ); | ||
923 | } | ||
924 | |||
925 | #[test] | ||
926 | fn tuple_of_empty_tuple_no_diagnostic() { | ||
927 | check_no_diagnostic( | ||
928 | r" | ||
929 | fn test_fn() { | ||
930 | match (()) { | ||
931 | (()) => {} | ||
932 | } | ||
933 | } | ||
934 | ", | ||
935 | ); | ||
936 | } | ||
937 | 841 | ||
938 | #[test] | 842 | #[test] |
939 | fn tuple_of_two_empty_tuple_no_arms() { | 843 | fn empty_tuple() { |
940 | check_diagnostic( | 844 | check_diagnostics( |
941 | r" | 845 | r#" |
942 | fn test_fn() { | 846 | fn main() { |
943 | match ((), ()) { | 847 | match () { } |
944 | } | 848 | //^^ Missing match arm |
945 | } | 849 | match (()) { } |
946 | ", | 850 | //^^^^ Missing match arm |
947 | ); | ||
948 | } | ||
949 | 851 | ||
950 | #[test] | 852 | match () { _ => (), } |
951 | fn tuple_of_two_empty_tuple_no_diagnostic() { | 853 | match () { () => (), } |
952 | check_no_diagnostic( | 854 | match (()) { (()) => (), } |
953 | r" | 855 | } |
954 | fn test_fn() { | 856 | "#, |
955 | match ((), ()) { | ||
956 | ((), ()) => {} | ||
957 | } | ||
958 | } | ||
959 | ", | ||
960 | ); | ||
961 | } | ||
962 | |||
963 | #[test] | ||
964 | fn bool_no_arms() { | ||
965 | check_diagnostic( | ||
966 | r" | ||
967 | fn test_fn() { | ||
968 | match false { | ||
969 | } | ||
970 | } | ||
971 | ", | ||
972 | ); | ||
973 | } | ||
974 | |||
975 | #[test] | ||
976 | fn bool_missing_arm() { | ||
977 | check_diagnostic( | ||
978 | r" | ||
979 | fn test_fn() { | ||
980 | match false { | ||
981 | true => {} | ||
982 | } | ||
983 | } | ||
984 | ", | ||
985 | ); | ||
986 | } | ||
987 | |||
988 | #[test] | ||
989 | fn bool_no_diagnostic() { | ||
990 | check_no_diagnostic( | ||
991 | r" | ||
992 | fn test_fn() { | ||
993 | match false { | ||
994 | true => {} | ||
995 | false => {} | ||
996 | } | ||
997 | } | ||
998 | ", | ||
999 | ); | ||
1000 | } | ||
1001 | |||
1002 | #[test] | ||
1003 | fn tuple_of_bools_no_arms() { | ||
1004 | check_diagnostic( | ||
1005 | r" | ||
1006 | fn test_fn() { | ||
1007 | match (false, true) { | ||
1008 | } | ||
1009 | } | ||
1010 | ", | ||
1011 | ); | ||
1012 | } | ||
1013 | |||
1014 | #[test] | ||
1015 | fn tuple_of_bools_missing_arms() { | ||
1016 | check_diagnostic( | ||
1017 | r" | ||
1018 | fn test_fn() { | ||
1019 | match (false, true) { | ||
1020 | (true, true) => {}, | ||
1021 | } | ||
1022 | } | ||
1023 | ", | ||
1024 | ); | ||
1025 | } | ||
1026 | |||
1027 | #[test] | ||
1028 | fn tuple_of_bools_missing_arm() { | ||
1029 | check_diagnostic( | ||
1030 | r" | ||
1031 | fn test_fn() { | ||
1032 | match (false, true) { | ||
1033 | (false, true) => {}, | ||
1034 | (false, false) => {}, | ||
1035 | (true, false) => {}, | ||
1036 | } | ||
1037 | } | ||
1038 | ", | ||
1039 | ); | ||
1040 | } | ||
1041 | |||
1042 | #[test] | ||
1043 | fn tuple_of_bools_with_wilds() { | ||
1044 | check_no_diagnostic( | ||
1045 | r" | ||
1046 | fn test_fn() { | ||
1047 | match (false, true) { | ||
1048 | (false, _) => {}, | ||
1049 | (true, false) => {}, | ||
1050 | (_, true) => {}, | ||
1051 | } | ||
1052 | } | ||
1053 | ", | ||
1054 | ); | 857 | ); |
1055 | } | 858 | } |
1056 | 859 | ||
1057 | #[test] | 860 | #[test] |
1058 | fn tuple_of_bools_no_diagnostic() { | 861 | fn tuple_of_two_empty_tuple() { |
1059 | check_no_diagnostic( | 862 | check_diagnostics( |
1060 | r" | 863 | r#" |
1061 | fn test_fn() { | 864 | fn main() { |
1062 | match (false, true) { | 865 | match ((), ()) { } |
1063 | (true, true) => {}, | 866 | //^^^^^^^^ Missing match arm |
1064 | (true, false) => {}, | ||
1065 | (false, true) => {}, | ||
1066 | (false, false) => {}, | ||
1067 | } | ||
1068 | } | ||
1069 | ", | ||
1070 | ); | ||
1071 | } | ||
1072 | 867 | ||
1073 | #[test] | 868 | match ((), ()) { ((), ()) => (), } |
1074 | fn tuple_of_bools_binding_missing_arms() { | 869 | } |
1075 | check_diagnostic( | 870 | "#, |
1076 | r" | 871 | ); |
1077 | fn test_fn() { | 872 | } |
1078 | match (false, true) { | 873 | |
1079 | (true, _x) => {}, | 874 | #[test] |
1080 | } | 875 | fn boolean() { |
1081 | } | 876 | check_diagnostics( |
1082 | ", | 877 | r#" |
1083 | ); | 878 | fn test_main() { |
1084 | } | 879 | match false { } |
1085 | 880 | //^^^^^ Missing match arm | |
1086 | #[test] | 881 | match false { true => (), } |
1087 | fn tuple_of_bools_binding_no_diagnostic() { | 882 | //^^^^^ Missing match arm |
1088 | check_no_diagnostic( | 883 | match (false, true) {} |
1089 | r" | 884 | //^^^^^^^^^^^^^ Missing match arm |
1090 | fn test_fn() { | 885 | match (false, true) { (true, true) => (), } |
1091 | match (false, true) { | 886 | //^^^^^^^^^^^^^ Missing match arm |
1092 | (true, _x) => {}, | 887 | match (false, true) { |
1093 | (false, true) => {}, | 888 | //^^^^^^^^^^^^^ Missing match arm |
1094 | (false, false) => {}, | 889 | (false, true) => (), |
1095 | } | 890 | (false, false) => (), |
1096 | } | 891 | (true, false) => (), |
1097 | ", | 892 | } |
893 | match (false, true) { (true, _x) => (), } | ||
894 | //^^^^^^^^^^^^^ Missing match arm | ||
895 | |||
896 | match false { true => (), false => (), } | ||
897 | match (false, true) { | ||
898 | (false, _) => (), | ||
899 | (true, false) => (), | ||
900 | (_, true) => (), | ||
901 | } | ||
902 | match (false, true) { | ||
903 | (true, true) => (), | ||
904 | (true, false) => (), | ||
905 | (false, true) => (), | ||
906 | (false, false) => (), | ||
907 | } | ||
908 | match (false, true) { | ||
909 | (true, _x) => (), | ||
910 | (false, true) => (), | ||
911 | (false, false) => (), | ||
912 | } | ||
913 | match (false, true, false) { | ||
914 | (false, ..) => (), | ||
915 | (true, ..) => (), | ||
916 | } | ||
917 | match (false, true, false) { | ||
918 | (.., false) => (), | ||
919 | (.., true) => (), | ||
920 | } | ||
921 | match (false, true, false) { (..) => (), } | ||
922 | } | ||
923 | "#, | ||
1098 | ); | 924 | ); |
1099 | } | 925 | } |
1100 | 926 | ||
1101 | #[test] | 927 | #[test] |
1102 | fn tuple_of_bools_with_ellipsis_at_end_no_diagnostic() { | 928 | fn tuple_of_tuple_and_bools() { |
1103 | check_no_diagnostic( | 929 | check_diagnostics( |
1104 | r" | 930 | r#" |
1105 | fn test_fn() { | 931 | fn main() { |
1106 | match (false, true, false) { | 932 | match (false, ((), false)) {} |
1107 | (false, ..) => {}, | 933 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm |
1108 | (true, ..) => {}, | 934 | match (false, ((), false)) { (true, ((), true)) => (), } |
1109 | } | 935 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm |
1110 | } | 936 | match (false, ((), false)) { (true, _) => (), } |
1111 | ", | 937 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm |
1112 | ); | ||
1113 | } | ||
1114 | 938 | ||
1115 | #[test] | 939 | match (false, ((), false)) { |
1116 | fn tuple_of_bools_with_ellipsis_at_beginning_no_diagnostic() { | 940 | (true, ((), true)) => (), |
1117 | check_no_diagnostic( | 941 | (true, ((), false)) => (), |
1118 | r" | 942 | (false, ((), true)) => (), |
1119 | fn test_fn() { | 943 | (false, ((), false)) => (), |
1120 | match (false, true, false) { | ||
1121 | (.., false) => {}, | ||
1122 | (.., true) => {}, | ||
1123 | } | ||
1124 | } | ||
1125 | ", | ||
1126 | ); | ||
1127 | } | 944 | } |
1128 | 945 | match (false, ((), false)) { | |
1129 | #[test] | 946 | (true, ((), true)) => (), |
1130 | fn tuple_of_bools_with_ellipsis_no_diagnostic() { | 947 | (true, ((), false)) => (), |
1131 | check_no_diagnostic( | 948 | (false, _) => (), |
1132 | r" | ||
1133 | fn test_fn() { | ||
1134 | match (false, true, false) { | ||
1135 | (..) => {}, | ||
1136 | } | ||
1137 | } | ||
1138 | ", | ||
1139 | ); | ||
1140 | } | 949 | } |
1141 | 950 | } | |
1142 | #[test] | 951 | "#, |
1143 | fn tuple_of_tuple_and_bools_no_arms() { | ||
1144 | check_diagnostic( | ||
1145 | r" | ||
1146 | fn test_fn() { | ||
1147 | match (false, ((), false)) { | ||
1148 | } | ||
1149 | } | ||
1150 | ", | ||
1151 | ); | 952 | ); |
1152 | } | 953 | } |
1153 | 954 | ||
1154 | #[test] | 955 | #[test] |
1155 | fn tuple_of_tuple_and_bools_missing_arms() { | 956 | fn enums() { |
1156 | check_diagnostic( | 957 | check_diagnostics( |
1157 | r" | 958 | r#" |
1158 | fn test_fn() { | 959 | enum Either { A, B, } |
1159 | match (false, ((), false)) { | ||
1160 | (true, ((), true)) => {}, | ||
1161 | } | ||
1162 | } | ||
1163 | ", | ||
1164 | ); | ||
1165 | } | ||
1166 | 960 | ||
1167 | #[test] | 961 | fn main() { |
1168 | fn tuple_of_tuple_and_bools_no_diagnostic() { | 962 | match Either::A { } |
1169 | check_no_diagnostic( | 963 | //^^^^^^^^^ Missing match arm |
1170 | r" | 964 | match Either::B { Either::A => (), } |
1171 | fn test_fn() { | 965 | //^^^^^^^^^ Missing match arm |
1172 | match (false, ((), false)) { | ||
1173 | (true, ((), true)) => {}, | ||
1174 | (true, ((), false)) => {}, | ||
1175 | (false, ((), true)) => {}, | ||
1176 | (false, ((), false)) => {}, | ||
1177 | } | ||
1178 | } | ||
1179 | ", | ||
1180 | ); | ||
1181 | } | ||
1182 | 966 | ||
1183 | #[test] | 967 | match &Either::B { |
1184 | fn tuple_of_tuple_and_bools_wildcard_missing_arms() { | 968 | //^^^^^^^^^^ Missing match arm |
1185 | check_diagnostic( | 969 | Either::A => (), |
1186 | r" | ||
1187 | fn test_fn() { | ||
1188 | match (false, ((), false)) { | ||
1189 | (true, _) => {}, | ||
1190 | } | ||
1191 | } | ||
1192 | ", | ||
1193 | ); | ||
1194 | } | 970 | } |
1195 | 971 | ||
1196 | #[test] | 972 | match Either::B { |
1197 | fn tuple_of_tuple_and_bools_wildcard_no_diagnostic() { | 973 | Either::A => (), Either::B => (), |
1198 | check_no_diagnostic( | ||
1199 | r" | ||
1200 | fn test_fn() { | ||
1201 | match (false, ((), false)) { | ||
1202 | (true, ((), true)) => {}, | ||
1203 | (true, ((), false)) => {}, | ||
1204 | (false, _) => {}, | ||
1205 | } | ||
1206 | } | ||
1207 | ", | ||
1208 | ); | ||
1209 | } | 974 | } |
1210 | 975 | match &Either::B { | |
1211 | #[test] | 976 | Either::A => (), Either::B => (), |
1212 | fn enum_no_arms() { | ||
1213 | check_diagnostic( | ||
1214 | r" | ||
1215 | enum Either { | ||
1216 | A, | ||
1217 | B, | ||
1218 | } | ||
1219 | fn test_fn() { | ||
1220 | match Either::A { | ||
1221 | } | ||
1222 | } | ||
1223 | ", | ||
1224 | ); | ||
1225 | } | 977 | } |
1226 | 978 | } | |
1227 | #[test] | 979 | "#, |
1228 | fn enum_missing_arms() { | ||
1229 | check_diagnostic( | ||
1230 | r" | ||
1231 | enum Either { | ||
1232 | A, | ||
1233 | B, | ||
1234 | } | ||
1235 | fn test_fn() { | ||
1236 | match Either::B { | ||
1237 | Either::A => {}, | ||
1238 | } | ||
1239 | } | ||
1240 | ", | ||
1241 | ); | 980 | ); |
1242 | } | 981 | } |
1243 | 982 | ||
1244 | #[test] | 983 | #[test] |
1245 | fn enum_no_diagnostic() { | 984 | fn enum_containing_bool() { |
1246 | check_no_diagnostic( | 985 | check_diagnostics( |
1247 | r" | 986 | r#" |
1248 | enum Either { | 987 | enum Either { A(bool), B } |
1249 | A, | ||
1250 | B, | ||
1251 | } | ||
1252 | fn test_fn() { | ||
1253 | match Either::B { | ||
1254 | Either::A => {}, | ||
1255 | Either::B => {}, | ||
1256 | } | ||
1257 | } | ||
1258 | ", | ||
1259 | ); | ||
1260 | } | ||
1261 | 988 | ||
1262 | #[test] | 989 | fn main() { |
1263 | fn enum_ref_missing_arms() { | 990 | match Either::B { } |
1264 | check_diagnostic( | 991 | //^^^^^^^^^ Missing match arm |
1265 | r" | 992 | match Either::B { |
1266 | enum Either { | 993 | //^^^^^^^^^ Missing match arm |
1267 | A, | 994 | Either::A(true) => (), Either::B => () |
1268 | B, | ||
1269 | } | ||
1270 | fn test_fn() { | ||
1271 | match &Either::B { | ||
1272 | Either::A => {}, | ||
1273 | } | ||
1274 | } | ||
1275 | ", | ||
1276 | ); | ||
1277 | } | 995 | } |
1278 | 996 | ||
1279 | #[test] | 997 | match Either::B { |
1280 | fn enum_ref_no_diagnostic() { | 998 | Either::A(true) => (), |
1281 | check_no_diagnostic( | 999 | Either::A(false) => (), |
1282 | r" | 1000 | Either::B => (), |
1283 | enum Either { | ||
1284 | A, | ||
1285 | B, | ||
1286 | } | ||
1287 | fn test_fn() { | ||
1288 | match &Either::B { | ||
1289 | Either::A => {}, | ||
1290 | Either::B => {}, | ||
1291 | } | ||
1292 | } | ||
1293 | ", | ||
1294 | ); | ||
1295 | } | 1001 | } |
1296 | 1002 | match Either::B { | |
1297 | #[test] | 1003 | Either::B => (), |
1298 | fn enum_containing_bool_no_arms() { | 1004 | _ => (), |
1299 | check_diagnostic( | ||
1300 | r" | ||
1301 | enum Either { | ||
1302 | A(bool), | ||
1303 | B, | ||
1304 | } | ||
1305 | fn test_fn() { | ||
1306 | match Either::B { | ||
1307 | } | ||
1308 | } | ||
1309 | ", | ||
1310 | ); | ||
1311 | } | 1005 | } |
1312 | 1006 | match Either::B { | |
1313 | #[test] | 1007 | Either::A(_) => (), |
1314 | fn enum_containing_bool_missing_arms() { | 1008 | Either::B => (), |
1315 | check_diagnostic( | ||
1316 | r" | ||
1317 | enum Either { | ||
1318 | A(bool), | ||
1319 | B, | ||
1320 | } | ||
1321 | fn test_fn() { | ||
1322 | match Either::B { | ||
1323 | Either::A(true) => (), | ||
1324 | Either::B => (), | ||
1325 | } | ||
1326 | } | ||
1327 | ", | ||
1328 | ); | ||
1329 | } | 1009 | } |
1330 | 1010 | ||
1331 | #[test] | 1011 | } |
1332 | fn enum_containing_bool_no_diagnostic() { | 1012 | "#, |
1333 | check_no_diagnostic( | ||
1334 | r" | ||
1335 | enum Either { | ||
1336 | A(bool), | ||
1337 | B, | ||
1338 | } | ||
1339 | fn test_fn() { | ||
1340 | match Either::B { | ||
1341 | Either::A(true) => (), | ||
1342 | Either::A(false) => (), | ||
1343 | Either::B => (), | ||
1344 | } | ||
1345 | } | ||
1346 | ", | ||
1347 | ); | 1013 | ); |
1348 | } | 1014 | } |
1349 | 1015 | ||
1350 | #[test] | 1016 | #[test] |
1351 | fn enum_containing_bool_with_wild_no_diagnostic() { | 1017 | fn enum_different_sizes() { |
1352 | check_no_diagnostic( | 1018 | check_diagnostics( |
1353 | r" | 1019 | r#" |
1354 | enum Either { | 1020 | enum Either { A(bool), B(bool, bool) } |
1355 | A(bool), | ||
1356 | B, | ||
1357 | } | ||
1358 | fn test_fn() { | ||
1359 | match Either::B { | ||
1360 | Either::B => (), | ||
1361 | _ => (), | ||
1362 | } | ||
1363 | } | ||
1364 | ", | ||
1365 | ); | ||
1366 | } | ||
1367 | 1021 | ||
1368 | #[test] | 1022 | fn main() { |
1369 | fn enum_containing_bool_with_wild_2_no_diagnostic() { | 1023 | match Either::A(false) { |
1370 | check_no_diagnostic( | 1024 | //^^^^^^^^^^^^^^^^ Missing match arm |
1371 | r" | 1025 | Either::A(_) => (), |
1372 | enum Either { | 1026 | Either::B(false, _) => (), |
1373 | A(bool), | ||
1374 | B, | ||
1375 | } | ||
1376 | fn test_fn() { | ||
1377 | match Either::B { | ||
1378 | Either::A(_) => (), | ||
1379 | Either::B => (), | ||
1380 | } | ||
1381 | } | ||
1382 | ", | ||
1383 | ); | ||
1384 | } | 1027 | } |
1385 | 1028 | ||
1386 | #[test] | 1029 | match Either::A(false) { |
1387 | fn enum_different_sizes_missing_arms() { | 1030 | Either::A(_) => (), |
1388 | check_diagnostic( | 1031 | Either::B(true, _) => (), |
1389 | r" | 1032 | Either::B(false, _) => (), |
1390 | enum Either { | ||
1391 | A(bool), | ||
1392 | B(bool, bool), | ||
1393 | } | ||
1394 | fn test_fn() { | ||
1395 | match Either::A(false) { | ||
1396 | Either::A(_) => (), | ||
1397 | Either::B(false, _) => (), | ||
1398 | } | ||
1399 | } | ||
1400 | ", | ||
1401 | ); | ||
1402 | } | 1033 | } |
1403 | 1034 | match Either::A(false) { | |
1404 | #[test] | 1035 | Either::A(true) | Either::A(false) => (), |
1405 | fn enum_different_sizes_no_diagnostic() { | 1036 | Either::B(true, _) => (), |
1406 | check_no_diagnostic( | 1037 | Either::B(false, _) => (), |
1407 | r" | ||
1408 | enum Either { | ||
1409 | A(bool), | ||
1410 | B(bool, bool), | ||
1411 | } | ||
1412 | fn test_fn() { | ||
1413 | match Either::A(false) { | ||
1414 | Either::A(_) => (), | ||
1415 | Either::B(true, _) => (), | ||
1416 | Either::B(false, _) => (), | ||
1417 | } | ||
1418 | } | ||
1419 | ", | ||
1420 | ); | ||
1421 | } | 1038 | } |
1422 | 1039 | } | |
1423 | #[test] | 1040 | "#, |
1424 | fn or_no_diagnostic() { | ||
1425 | check_no_diagnostic( | ||
1426 | r" | ||
1427 | enum Either { | ||
1428 | A(bool), | ||
1429 | B(bool, bool), | ||
1430 | } | ||
1431 | fn test_fn() { | ||
1432 | match Either::A(false) { | ||
1433 | Either::A(true) | Either::A(false) => (), | ||
1434 | Either::B(true, _) => (), | ||
1435 | Either::B(false, _) => (), | ||
1436 | } | ||
1437 | } | ||
1438 | ", | ||
1439 | ); | 1041 | ); |
1440 | } | 1042 | } |
1441 | 1043 | ||
1442 | #[test] | 1044 | #[test] |
1443 | fn tuple_of_enum_no_diagnostic() { | 1045 | fn tuple_of_enum_no_diagnostic() { |
1444 | check_no_diagnostic( | 1046 | check_diagnostics( |
1445 | r" | 1047 | r#" |
1446 | enum Either { | 1048 | enum Either { A(bool), B(bool, bool) } |
1447 | A(bool), | 1049 | enum Either2 { C, D } |
1448 | B(bool, bool), | 1050 | |
1449 | } | 1051 | fn main() { |
1450 | enum Either2 { | 1052 | match (Either::A(false), Either2::C) { |
1451 | C, | 1053 | (Either::A(true), _) | (Either::A(false), _) => (), |
1452 | D, | 1054 | (Either::B(true, _), Either2::C) => (), |
1453 | } | 1055 | (Either::B(false, _), Either2::C) => (), |
1454 | fn test_fn() { | 1056 | (Either::B(_, _), Either2::D) => (), |
1455 | match (Either::A(false), Either2::C) { | ||
1456 | (Either::A(true), _) | (Either::A(false), _) => (), | ||
1457 | (Either::B(true, _), Either2::C) => (), | ||
1458 | (Either::B(false, _), Either2::C) => (), | ||
1459 | (Either::B(_, _), Either2::D) => (), | ||
1460 | } | ||
1461 | } | ||
1462 | ", | ||
1463 | ); | ||
1464 | } | ||
1465 | |||
1466 | #[test] | ||
1467 | fn mismatched_types() { | ||
1468 | // Match statements with arms that don't match the | ||
1469 | // expression pattern do not fire this diagnostic. | ||
1470 | check_no_diagnostic( | ||
1471 | r" | ||
1472 | enum Either { | ||
1473 | A, | ||
1474 | B, | ||
1475 | } | ||
1476 | enum Either2 { | ||
1477 | C, | ||
1478 | D, | ||
1479 | } | ||
1480 | fn test_fn() { | ||
1481 | match Either::A { | ||
1482 | Either2::C => (), | ||
1483 | Either2::D => (), | ||
1484 | } | ||
1485 | } | ||
1486 | ", | ||
1487 | ); | ||
1488 | } | 1057 | } |
1489 | 1058 | } | |
1490 | #[test] | 1059 | "#, |
1491 | fn mismatched_types_with_different_arity() { | ||
1492 | // Match statements with arms that don't match the | ||
1493 | // expression pattern do not fire this diagnostic. | ||
1494 | check_no_diagnostic( | ||
1495 | r" | ||
1496 | fn test_fn() { | ||
1497 | match (true, false) { | ||
1498 | (true, false, true) => (), | ||
1499 | (true) => (), | ||
1500 | } | ||
1501 | } | ||
1502 | ", | ||
1503 | ); | 1060 | ); |
1504 | } | 1061 | } |
1505 | 1062 | ||
1506 | #[test] | 1063 | #[test] |
1507 | fn malformed_match_arm_tuple_missing_pattern() { | 1064 | fn mismatched_types() { |
1508 | // Match statements with arms that don't match the | 1065 | // Match statements with arms that don't match the |
1509 | // expression pattern do not fire this diagnostic. | 1066 | // expression pattern do not fire this diagnostic. |
1510 | check_no_diagnostic( | 1067 | check_diagnostics( |
1511 | r" | 1068 | r#" |
1512 | fn test_fn() { | 1069 | enum Either { A, B } |
1513 | match (0) { | 1070 | enum Either2 { C, D } |
1514 | () => (), | 1071 | |
1515 | } | 1072 | fn main() { |
1516 | } | 1073 | match Either::A { |
1517 | ", | 1074 | Either2::C => (), |
1075 | Either2::D => (), | ||
1076 | } | ||
1077 | match (true, false) { | ||
1078 | (true, false, true) => (), | ||
1079 | (true) => (), | ||
1080 | } | ||
1081 | match (0) { () => () } | ||
1082 | match Unresolved::Bar { Unresolved::Baz => () } | ||
1083 | } | ||
1084 | "#, | ||
1518 | ); | 1085 | ); |
1519 | } | 1086 | } |
1520 | 1087 | ||
@@ -1522,636 +1089,333 @@ mod tests { | |||
1522 | fn malformed_match_arm_tuple_enum_missing_pattern() { | 1089 | fn malformed_match_arm_tuple_enum_missing_pattern() { |
1523 | // We are testing to be sure we don't panic here when the match | 1090 | // We are testing to be sure we don't panic here when the match |
1524 | // arm `Either::B` is missing its pattern. | 1091 | // arm `Either::B` is missing its pattern. |
1525 | check_no_diagnostic( | 1092 | check_diagnostics( |
1526 | r" | 1093 | r#" |
1527 | enum Either { | 1094 | enum Either { A, B(u32) } |
1528 | A, | ||
1529 | B(u32), | ||
1530 | } | ||
1531 | fn test_fn() { | ||
1532 | match Either::A { | ||
1533 | Either::A => (), | ||
1534 | Either::B() => (), | ||
1535 | } | ||
1536 | } | ||
1537 | ", | ||
1538 | ); | ||
1539 | } | ||
1540 | 1095 | ||
1541 | #[test] | 1096 | fn main() { |
1542 | fn enum_not_in_scope() { | 1097 | match Either::A { |
1543 | // The enum is not in scope so we don't perform exhaustiveness | 1098 | Either::A => (), |
1544 | // checking, but we want to be sure we don't panic here (and | 1099 | Either::B() => (), |
1545 | // we don't create a diagnostic). | 1100 | } |
1546 | check_no_diagnostic( | 1101 | } |
1547 | r" | 1102 | "#, |
1548 | fn test_fn() { | ||
1549 | match Foo::Bar { | ||
1550 | Foo::Baz => (), | ||
1551 | } | ||
1552 | } | ||
1553 | ", | ||
1554 | ); | 1103 | ); |
1555 | } | 1104 | } |
1556 | 1105 | ||
1557 | #[test] | 1106 | #[test] |
1558 | fn expr_diverges() { | 1107 | fn expr_diverges() { |
1559 | check_no_diagnostic( | 1108 | check_diagnostics( |
1560 | r" | 1109 | r#" |
1561 | enum Either { | 1110 | enum Either { A, B } |
1562 | A, | ||
1563 | B, | ||
1564 | } | ||
1565 | fn test_fn() { | ||
1566 | match loop {} { | ||
1567 | Either::A => (), | ||
1568 | Either::B => (), | ||
1569 | } | ||
1570 | } | ||
1571 | ", | ||
1572 | ); | ||
1573 | } | ||
1574 | 1111 | ||
1575 | #[test] | 1112 | fn main() { |
1576 | fn expr_loop_with_break() { | 1113 | match loop {} { |
1577 | check_no_diagnostic( | 1114 | Either::A => (), |
1578 | r" | 1115 | Either::B => (), |
1579 | enum Either { | 1116 | } |
1580 | A, | 1117 | match loop {} { |
1581 | B, | 1118 | Either::A => (), |
1582 | } | 1119 | } |
1583 | fn test_fn() { | 1120 | match loop { break Foo::A } { |
1584 | match loop { break Foo::A } { | 1121 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm |
1585 | Either::A => (), | 1122 | Either::A => (), |
1586 | Either::B => (), | 1123 | } |
1587 | } | 1124 | match loop { break Foo::A } { |
1588 | } | 1125 | Either::A => (), |
1589 | ", | 1126 | Either::B => (), |
1127 | } | ||
1128 | } | ||
1129 | "#, | ||
1590 | ); | 1130 | ); |
1591 | } | 1131 | } |
1592 | 1132 | ||
1593 | #[test] | 1133 | #[test] |
1594 | fn expr_partially_diverges() { | 1134 | fn expr_partially_diverges() { |
1595 | check_no_diagnostic( | 1135 | check_diagnostics( |
1596 | r" | 1136 | r#" |
1597 | enum Either<T> { | 1137 | enum Either<T> { A(T), B } |
1598 | A(T), | ||
1599 | B, | ||
1600 | } | ||
1601 | fn foo() -> Either<!> { | ||
1602 | Either::B | ||
1603 | } | ||
1604 | fn test_fn() -> u32 { | ||
1605 | match foo() { | ||
1606 | Either::A(val) => val, | ||
1607 | Either::B => 0, | ||
1608 | } | ||
1609 | } | ||
1610 | ", | ||
1611 | ); | ||
1612 | } | ||
1613 | 1138 | ||
1614 | #[test] | 1139 | fn foo() -> Either<!> { Either::B } |
1615 | fn enum_record_no_arms() { | 1140 | fn main() -> u32 { |
1616 | check_diagnostic( | 1141 | match foo() { |
1617 | r" | 1142 | Either::A(val) => val, |
1618 | enum Either { | 1143 | Either::B => 0, |
1619 | A { foo: bool }, | ||
1620 | B, | ||
1621 | } | ||
1622 | fn test_fn() { | ||
1623 | let a = Either::A { foo: true }; | ||
1624 | match a { | ||
1625 | } | ||
1626 | } | ||
1627 | ", | ||
1628 | ); | ||
1629 | } | 1144 | } |
1630 | 1145 | } | |
1631 | #[test] | 1146 | "#, |
1632 | fn enum_record_missing_arms() { | ||
1633 | check_diagnostic( | ||
1634 | r" | ||
1635 | enum Either { | ||
1636 | A { foo: bool }, | ||
1637 | B, | ||
1638 | } | ||
1639 | fn test_fn() { | ||
1640 | let a = Either::A { foo: true }; | ||
1641 | match a { | ||
1642 | Either::A { foo: true } => (), | ||
1643 | } | ||
1644 | } | ||
1645 | ", | ||
1646 | ); | 1147 | ); |
1647 | } | 1148 | } |
1648 | 1149 | ||
1649 | #[test] | 1150 | #[test] |
1650 | fn enum_record_no_diagnostic() { | 1151 | fn enum_record() { |
1651 | check_no_diagnostic( | 1152 | check_diagnostics( |
1652 | r" | 1153 | r#" |
1653 | enum Either { | 1154 | enum Either { A { foo: bool }, B } |
1654 | A { foo: bool }, | ||
1655 | B, | ||
1656 | } | ||
1657 | fn test_fn() { | ||
1658 | let a = Either::A { foo: true }; | ||
1659 | match a { | ||
1660 | Either::A { foo: true } => (), | ||
1661 | Either::A { foo: false } => (), | ||
1662 | Either::B => (), | ||
1663 | } | ||
1664 | } | ||
1665 | ", | ||
1666 | ); | ||
1667 | } | ||
1668 | 1155 | ||
1669 | #[test] | 1156 | fn main() { |
1670 | fn enum_record_missing_field_no_diagnostic() { | 1157 | let a = Either::A { foo: true }; |
1671 | // When `Either::A` is missing a struct member, we don't want | 1158 | match a { } |
1672 | // to fire the missing match arm diagnostic. This should fire | 1159 | //^ Missing match arm |
1673 | // some other diagnostic. | 1160 | match a { Either::A { foo: true } => () } |
1674 | check_no_diagnostic( | 1161 | //^ Missing match arm |
1675 | r" | 1162 | match a { |
1676 | enum Either { | 1163 | Either::A { } => (), |
1677 | A { foo: bool }, | 1164 | //^^^ Missing structure fields: |
1678 | B, | 1165 | // | - foo |
1679 | } | 1166 | Either::B => (), |
1680 | fn test_fn() { | ||
1681 | let a = Either::B; | ||
1682 | match a { | ||
1683 | Either::A { } => (), | ||
1684 | Either::B => (), | ||
1685 | } | ||
1686 | } | ||
1687 | ", | ||
1688 | ); | ||
1689 | } | 1167 | } |
1168 | match a { | ||
1169 | //^ Missing match arm | ||
1170 | Either::A { } => (), | ||
1171 | } //^^^ Missing structure fields: | ||
1172 | // | - foo | ||
1690 | 1173 | ||
1691 | #[test] | 1174 | match a { |
1692 | fn enum_record_missing_field_missing_match_arm() { | 1175 | Either::A { foo: true } => (), |
1693 | // Even though `Either::A` is missing fields, we still want to fire | 1176 | Either::A { foo: false } => (), |
1694 | // the missing arm diagnostic here, since we know `Either::B` is missing. | 1177 | Either::B => (), |
1695 | check_diagnostic( | ||
1696 | r" | ||
1697 | enum Either { | ||
1698 | A { foo: bool }, | ||
1699 | B, | ||
1700 | } | ||
1701 | fn test_fn() { | ||
1702 | let a = Either::B; | ||
1703 | match a { | ||
1704 | Either::A { } => (), | ||
1705 | } | ||
1706 | } | ||
1707 | ", | ||
1708 | ); | ||
1709 | } | 1178 | } |
1710 | 1179 | match a { | |
1711 | #[test] | 1180 | Either::A { foo: _ } => (), |
1712 | fn enum_record_no_diagnostic_wild() { | 1181 | Either::B => (), |
1713 | check_no_diagnostic( | ||
1714 | r" | ||
1715 | enum Either { | ||
1716 | A { foo: bool }, | ||
1717 | B, | ||
1718 | } | ||
1719 | fn test_fn() { | ||
1720 | let a = Either::A { foo: true }; | ||
1721 | match a { | ||
1722 | Either::A { foo: _ } => (), | ||
1723 | Either::B => (), | ||
1724 | } | ||
1725 | } | ||
1726 | ", | ||
1727 | ); | ||
1728 | } | 1182 | } |
1729 | 1183 | } | |
1730 | #[test] | 1184 | "#, |
1731 | fn enum_record_fields_out_of_order_missing_arm() { | ||
1732 | check_diagnostic( | ||
1733 | r" | ||
1734 | enum Either { | ||
1735 | A { foo: bool, bar: () }, | ||
1736 | B, | ||
1737 | } | ||
1738 | fn test_fn() { | ||
1739 | let a = Either::A { foo: true }; | ||
1740 | match a { | ||
1741 | Either::A { bar: (), foo: false } => (), | ||
1742 | Either::A { foo: true, bar: () } => (), | ||
1743 | } | ||
1744 | } | ||
1745 | ", | ||
1746 | ); | 1185 | ); |
1747 | } | 1186 | } |
1748 | 1187 | ||
1749 | #[test] | 1188 | #[test] |
1750 | fn enum_record_fields_out_of_order_no_diagnostic() { | 1189 | fn enum_record_fields_out_of_order() { |
1751 | check_no_diagnostic( | 1190 | check_diagnostics( |
1752 | r" | 1191 | r#" |
1753 | enum Either { | 1192 | enum Either { |
1754 | A { foo: bool, bar: () }, | 1193 | A { foo: bool, bar: () }, |
1755 | B, | 1194 | B, |
1756 | } | 1195 | } |
1757 | fn test_fn() { | ||
1758 | let a = Either::A { foo: true }; | ||
1759 | match a { | ||
1760 | Either::A { bar: (), foo: false } => (), | ||
1761 | Either::A { foo: true, bar: () } => (), | ||
1762 | Either::B => (), | ||
1763 | } | ||
1764 | } | ||
1765 | ", | ||
1766 | ); | ||
1767 | } | ||
1768 | 1196 | ||
1769 | #[test] | 1197 | fn main() { |
1770 | fn enum_record_ellipsis_missing_arm() { | 1198 | let a = Either::A { foo: true, bar: () }; |
1771 | check_diagnostic( | 1199 | match a { |
1772 | r" | 1200 | //^ Missing match arm |
1773 | enum Either { | 1201 | Either::A { bar: (), foo: false } => (), |
1774 | A { foo: bool, bar: bool }, | 1202 | Either::A { foo: true, bar: () } => (), |
1775 | B, | ||
1776 | } | ||
1777 | fn test_fn() { | ||
1778 | match Either::B { | ||
1779 | Either::A { foo: true, .. } => (), | ||
1780 | Either::B => (), | ||
1781 | } | ||
1782 | } | ||
1783 | ", | ||
1784 | ); | ||
1785 | } | 1203 | } |
1786 | 1204 | ||
1787 | #[test] | 1205 | match a { |
1788 | fn enum_record_ellipsis_no_diagnostic() { | 1206 | Either::A { bar: (), foo: false } => (), |
1789 | check_no_diagnostic( | 1207 | Either::A { foo: true, bar: () } => (), |
1790 | r" | 1208 | Either::B => (), |
1791 | enum Either { | ||
1792 | A { foo: bool, bar: bool }, | ||
1793 | B, | ||
1794 | } | ||
1795 | fn test_fn() { | ||
1796 | let a = Either::A { foo: true }; | ||
1797 | match a { | ||
1798 | Either::A { foo: true, .. } => (), | ||
1799 | Either::A { foo: false, .. } => (), | ||
1800 | Either::B => (), | ||
1801 | } | ||
1802 | } | ||
1803 | ", | ||
1804 | ); | ||
1805 | } | 1209 | } |
1806 | 1210 | } | |
1807 | #[test] | 1211 | "#, |
1808 | fn enum_record_ellipsis_all_fields_missing_arm() { | ||
1809 | check_diagnostic( | ||
1810 | r" | ||
1811 | enum Either { | ||
1812 | A { foo: bool, bar: bool }, | ||
1813 | B, | ||
1814 | } | ||
1815 | fn test_fn() { | ||
1816 | let a = Either::B; | ||
1817 | match a { | ||
1818 | Either::A { .. } => (), | ||
1819 | } | ||
1820 | } | ||
1821 | ", | ||
1822 | ); | 1212 | ); |
1823 | } | 1213 | } |
1824 | 1214 | ||
1825 | #[test] | 1215 | #[test] |
1826 | fn enum_record_ellipsis_all_fields_no_diagnostic() { | 1216 | fn enum_record_ellipsis() { |
1827 | check_no_diagnostic( | 1217 | check_diagnostics( |
1828 | r" | 1218 | r#" |
1829 | enum Either { | 1219 | enum Either { |
1830 | A { foo: bool, bar: bool }, | 1220 | A { foo: bool, bar: bool }, |
1831 | B, | 1221 | B, |
1832 | } | 1222 | } |
1833 | fn test_fn() { | ||
1834 | let a = Either::B; | ||
1835 | match a { | ||
1836 | Either::A { .. } => (), | ||
1837 | Either::B => (), | ||
1838 | } | ||
1839 | } | ||
1840 | ", | ||
1841 | ); | ||
1842 | } | ||
1843 | 1223 | ||
1844 | #[test] | 1224 | fn main() { |
1845 | fn enum_tuple_partial_ellipsis_no_diagnostic() { | 1225 | let a = Either::B; |
1846 | check_no_diagnostic( | 1226 | match a { |
1847 | r" | 1227 | //^ Missing match arm |
1848 | enum Either { | 1228 | Either::A { foo: true, .. } => (), |
1849 | A(bool, bool, bool, bool), | 1229 | Either::B => (), |
1850 | B, | ||
1851 | } | ||
1852 | fn test_fn() { | ||
1853 | match Either::B { | ||
1854 | Either::A(true, .., true) => {}, | ||
1855 | Either::A(true, .., false) => {}, | ||
1856 | Either::A(false, .., true) => {}, | ||
1857 | Either::A(false, .., false) => {}, | ||
1858 | Either::B => {}, | ||
1859 | } | ||
1860 | } | ||
1861 | ", | ||
1862 | ); | ||
1863 | } | 1230 | } |
1864 | 1231 | match a { | |
1865 | #[test] | 1232 | //^ Missing match arm |
1866 | fn enum_tuple_partial_ellipsis_2_no_diagnostic() { | 1233 | Either::A { .. } => (), |
1867 | check_no_diagnostic( | ||
1868 | r" | ||
1869 | enum Either { | ||
1870 | A(bool, bool, bool, bool), | ||
1871 | B, | ||
1872 | } | ||
1873 | fn test_fn() { | ||
1874 | match Either::B { | ||
1875 | Either::A(true, .., true) => {}, | ||
1876 | Either::A(true, .., false) => {}, | ||
1877 | Either::A(.., true) => {}, | ||
1878 | Either::A(.., false) => {}, | ||
1879 | Either::B => {}, | ||
1880 | } | ||
1881 | } | ||
1882 | ", | ||
1883 | ); | ||
1884 | } | 1234 | } |
1885 | 1235 | ||
1886 | #[test] | 1236 | match a { |
1887 | fn enum_tuple_partial_ellipsis_missing_arm() { | 1237 | Either::A { foo: true, .. } => (), |
1888 | check_diagnostic( | 1238 | Either::A { foo: false, .. } => (), |
1889 | r" | 1239 | Either::B => (), |
1890 | enum Either { | ||
1891 | A(bool, bool, bool, bool), | ||
1892 | B, | ||
1893 | } | ||
1894 | fn test_fn() { | ||
1895 | match Either::B { | ||
1896 | Either::A(true, .., true) => {}, | ||
1897 | Either::A(true, .., false) => {}, | ||
1898 | Either::A(false, .., false) => {}, | ||
1899 | Either::B => {}, | ||
1900 | } | ||
1901 | } | ||
1902 | ", | ||
1903 | ); | ||
1904 | } | 1240 | } |
1905 | 1241 | ||
1906 | #[test] | 1242 | match a { |
1907 | fn enum_tuple_partial_ellipsis_2_missing_arm() { | 1243 | Either::A { .. } => (), |
1908 | check_diagnostic( | 1244 | Either::B => (), |
1909 | r" | ||
1910 | enum Either { | ||
1911 | A(bool, bool, bool, bool), | ||
1912 | B, | ||
1913 | } | ||
1914 | fn test_fn() { | ||
1915 | match Either::B { | ||
1916 | Either::A(true, .., true) => {}, | ||
1917 | Either::A(true, .., false) => {}, | ||
1918 | Either::A(.., true) => {}, | ||
1919 | Either::B => {}, | ||
1920 | } | ||
1921 | } | ||
1922 | ", | ||
1923 | ); | ||
1924 | } | 1245 | } |
1925 | 1246 | } | |
1926 | #[test] | 1247 | "#, |
1927 | fn enum_tuple_ellipsis_no_diagnostic() { | ||
1928 | check_no_diagnostic( | ||
1929 | r" | ||
1930 | enum Either { | ||
1931 | A(bool, bool, bool, bool), | ||
1932 | B, | ||
1933 | } | ||
1934 | fn test_fn() { | ||
1935 | match Either::B { | ||
1936 | Either::A(..) => {}, | ||
1937 | Either::B => {}, | ||
1938 | } | ||
1939 | } | ||
1940 | ", | ||
1941 | ); | 1248 | ); |
1942 | } | 1249 | } |
1943 | 1250 | ||
1944 | #[test] | 1251 | #[test] |
1945 | fn enum_never() { | 1252 | fn enum_tuple_partial_ellipsis() { |
1946 | check_no_diagnostic( | 1253 | check_diagnostics( |
1947 | r" | 1254 | r#" |
1948 | enum Never {} | 1255 | enum Either { |
1256 | A(bool, bool, bool, bool), | ||
1257 | B, | ||
1258 | } | ||
1949 | 1259 | ||
1950 | fn test_fn(never: Never) { | 1260 | fn main() { |
1951 | match never {} | 1261 | match Either::B { |
1952 | } | 1262 | //^^^^^^^^^ Missing match arm |
1953 | ", | 1263 | Either::A(true, .., true) => (), |
1954 | ); | 1264 | Either::A(true, .., false) => (), |
1265 | Either::A(false, .., false) => (), | ||
1266 | Either::B => (), | ||
1267 | } | ||
1268 | match Either::B { | ||
1269 | //^^^^^^^^^ Missing match arm | ||
1270 | Either::A(true, .., true) => (), | ||
1271 | Either::A(true, .., false) => (), | ||
1272 | Either::A(.., true) => (), | ||
1273 | Either::B => (), | ||
1274 | } | ||
1275 | |||
1276 | match Either::B { | ||
1277 | Either::A(true, .., true) => (), | ||
1278 | Either::A(true, .., false) => (), | ||
1279 | Either::A(false, .., true) => (), | ||
1280 | Either::A(false, .., false) => (), | ||
1281 | Either::B => (), | ||
1282 | } | ||
1283 | match Either::B { | ||
1284 | Either::A(true, .., true) => (), | ||
1285 | Either::A(true, .., false) => (), | ||
1286 | Either::A(.., true) => (), | ||
1287 | Either::A(.., false) => (), | ||
1288 | Either::B => (), | ||
1955 | } | 1289 | } |
1956 | 1290 | } | |
1957 | #[test] | 1291 | "#, |
1958 | fn type_never() { | ||
1959 | check_no_diagnostic( | ||
1960 | r" | ||
1961 | fn test_fn(never: !) { | ||
1962 | match never {} | ||
1963 | } | ||
1964 | ", | ||
1965 | ); | 1292 | ); |
1966 | } | 1293 | } |
1967 | 1294 | ||
1968 | #[test] | 1295 | #[test] |
1969 | fn enum_never_ref() { | 1296 | fn never() { |
1970 | check_no_diagnostic( | 1297 | check_diagnostics( |
1971 | r" | 1298 | r#" |
1972 | enum Never {} | 1299 | enum Never {} |
1973 | 1300 | ||
1974 | fn test_fn(never: &Never) { | 1301 | fn enum_(never: Never) { |
1975 | match never {} | 1302 | match never {} |
1976 | } | 1303 | } |
1977 | ", | 1304 | fn enum_ref(never: &Never) { |
1978 | ); | 1305 | match never {} |
1979 | } | 1306 | } |
1980 | 1307 | fn bang(never: !) { | |
1981 | #[test] | 1308 | match never {} |
1982 | fn expr_diverges_missing_arm() { | 1309 | } |
1983 | check_no_diagnostic( | 1310 | "#, |
1984 | r" | ||
1985 | enum Either { | ||
1986 | A, | ||
1987 | B, | ||
1988 | } | ||
1989 | fn test_fn() { | ||
1990 | match loop {} { | ||
1991 | Either::A => (), | ||
1992 | } | ||
1993 | } | ||
1994 | ", | ||
1995 | ); | 1311 | ); |
1996 | } | 1312 | } |
1997 | 1313 | ||
1998 | #[test] | 1314 | #[test] |
1999 | fn or_pattern_panic() { | 1315 | fn or_pattern_panic() { |
2000 | check_no_diagnostic( | 1316 | check_diagnostics( |
2001 | r" | 1317 | r#" |
2002 | pub enum Category { | 1318 | pub enum Category { Infinity, Zero } |
2003 | Infinity, | ||
2004 | Zero, | ||
2005 | } | ||
2006 | 1319 | ||
2007 | fn panic(a: Category, b: Category) { | 1320 | fn panic(a: Category, b: Category) { |
2008 | match (a, b) { | 1321 | match (a, b) { |
2009 | (Category::Zero | Category::Infinity, _) => {} | 1322 | (Category::Zero | Category::Infinity, _) => (), |
2010 | (_, Category::Zero | Category::Infinity) => {} | 1323 | (_, Category::Zero | Category::Infinity) => (), |
2011 | } | ||
2012 | } | ||
2013 | ", | ||
2014 | ); | ||
2015 | } | 1324 | } |
2016 | 1325 | ||
2017 | #[test] | 1326 | // FIXME: This is a false positive, but the code used to cause a panic in the match checker, |
2018 | fn or_pattern_panic_2() { | 1327 | // so this acts as a regression test for that. |
2019 | // FIXME: This is a false positive, but the code used to cause a panic in the match checker, | 1328 | match (a, b) { |
2020 | // so this acts as a regression test for that. | 1329 | //^^^^^^ Missing match arm |
2021 | check_diagnostic( | 1330 | (Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => (), |
2022 | r" | 1331 | (Category::Infinity | Category::Zero, _) => (), |
2023 | pub enum Category { | ||
2024 | Infinity, | ||
2025 | Zero, | ||
2026 | } | ||
2027 | |||
2028 | fn panic(a: Category, b: Category) { | ||
2029 | match (a, b) { | ||
2030 | (Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => {} | ||
2031 | |||
2032 | (Category::Infinity | Category::Zero, _) => {} | ||
2033 | } | ||
2034 | } | ||
2035 | ", | ||
2036 | ); | ||
2037 | } | 1332 | } |
2038 | } | 1333 | } |
2039 | 1334 | "#, | |
2040 | #[cfg(test)] | 1335 | ); |
2041 | mod false_negatives { | 1336 | } |
2042 | //! The implementation of match checking here is a work in progress. As we roll this out, we | 1337 | |
2043 | //! prefer false negatives to false positives (ideally there would be no false positives). This | 1338 | mod false_negatives { |
2044 | //! test module should document known false negatives. Eventually we will have a complete | 1339 | //! The implementation of match checking here is a work in progress. As we roll this out, we |
2045 | //! implementation of match checking and this module will be empty. | 1340 | //! prefer false negatives to false positives (ideally there would be no false positives). This |
2046 | //! | 1341 | //! test module should document known false negatives. Eventually we will have a complete |
2047 | //! The reasons for documenting known false negatives: | 1342 | //! implementation of match checking and this module will be empty. |
2048 | //! | 1343 | //! |
2049 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. | 1344 | //! The reasons for documenting known false negatives: |
2050 | //! 2. It ensures the code doesn't panic when handling these cases. | 1345 | //! |
2051 | 1346 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. | |
2052 | use super::tests::*; | 1347 | //! 2. It ensures the code doesn't panic when handling these cases. |
2053 | 1348 | use super::*; | |
2054 | #[test] | 1349 | |
2055 | fn integers() { | 1350 | #[test] |
2056 | // This is a false negative. | 1351 | fn integers() { |
2057 | // We don't currently check integer exhaustiveness. | 1352 | // We don't currently check integer exhaustiveness. |
2058 | check_no_diagnostic( | 1353 | check_diagnostics( |
2059 | r" | 1354 | r#" |
2060 | fn test_fn() { | 1355 | fn main() { |
2061 | match 5 { | 1356 | match 5 { |
2062 | 10 => (), | 1357 | 10 => (), |
2063 | 11..20 => (), | 1358 | 11..20 => (), |
2064 | } | ||
2065 | } | ||
2066 | ", | ||
2067 | ); | ||
2068 | } | ||
2069 | |||
2070 | #[test] | ||
2071 | fn internal_or() { | ||
2072 | // This is a false negative. | ||
2073 | // We do not currently handle patterns with internal `or`s. | ||
2074 | check_no_diagnostic( | ||
2075 | r" | ||
2076 | fn test_fn() { | ||
2077 | enum Either { | ||
2078 | A(bool), | ||
2079 | B, | ||
2080 | } | ||
2081 | match Either::B { | ||
2082 | Either::A(true | false) => (), | ||
2083 | } | ||
2084 | } | ||
2085 | ", | ||
2086 | ); | ||
2087 | } | 1359 | } |
1360 | } | ||
1361 | "#, | ||
1362 | ); | ||
1363 | } | ||
2088 | 1364 | ||
2089 | #[test] | 1365 | #[test] |
2090 | fn expr_loop_missing_arm() { | 1366 | fn internal_or() { |
2091 | // This is a false negative. | 1367 | // We do not currently handle patterns with internal `or`s. |
2092 | // We currently infer the type of `loop { break Foo::A }` to `!`, which | 1368 | check_diagnostics( |
2093 | // causes us to skip the diagnostic since `Either::A` doesn't type check | 1369 | r#" |
2094 | // with `!`. | 1370 | fn main() { |
2095 | check_diagnostic( | 1371 | enum Either { A(bool), B } |
2096 | r" | 1372 | match Either::B { |
2097 | enum Either { | 1373 | Either::A(true | false) => (), |
2098 | A, | ||
2099 | B, | ||
2100 | } | ||
2101 | fn test_fn() { | ||
2102 | match loop { break Foo::A } { | ||
2103 | Either::A => (), | ||
2104 | } | ||
2105 | } | ||
2106 | ", | ||
2107 | ); | ||
2108 | } | 1374 | } |
1375 | } | ||
1376 | "#, | ||
1377 | ); | ||
1378 | } | ||
2109 | 1379 | ||
2110 | #[test] | 1380 | #[test] |
2111 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | 1381 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { |
2112 | // This is a false negative. | 1382 | // We don't currently handle tuple patterns with ellipsis. |
2113 | // We don't currently handle tuple patterns with ellipsis. | 1383 | check_diagnostics( |
2114 | check_no_diagnostic( | 1384 | r#" |
2115 | r" | 1385 | fn main() { |
2116 | fn test_fn() { | 1386 | match (false, true, false) { |
2117 | match (false, true, false) { | 1387 | (false, ..) => (), |
2118 | (false, ..) => {}, | ||
2119 | } | ||
2120 | } | ||
2121 | ", | ||
2122 | ); | ||
2123 | } | 1388 | } |
1389 | } | ||
1390 | "#, | ||
1391 | ); | ||
1392 | } | ||
2124 | 1393 | ||
2125 | #[test] | 1394 | #[test] |
2126 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | 1395 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { |
2127 | // This is a false negative. | 1396 | // We don't currently handle tuple patterns with ellipsis. |
2128 | // We don't currently handle tuple patterns with ellipsis. | 1397 | check_diagnostics( |
2129 | check_no_diagnostic( | 1398 | r#" |
2130 | r" | 1399 | fn main() { |
2131 | fn test_fn() { | 1400 | match (false, true, false) { |
2132 | match (false, true, false) { | 1401 | (.., false) => (), |
2133 | (.., false) => {}, | ||
2134 | } | ||
2135 | } | ||
2136 | ", | ||
2137 | ); | ||
2138 | } | 1402 | } |
1403 | } | ||
1404 | "#, | ||
1405 | ); | ||
1406 | } | ||
2139 | 1407 | ||
2140 | #[test] | 1408 | #[test] |
2141 | fn struct_missing_arm() { | 1409 | fn struct_missing_arm() { |
2142 | // This is a false negative. | 1410 | // We don't currently handle structs. |
2143 | // We don't currently handle structs. | 1411 | check_diagnostics( |
2144 | check_no_diagnostic( | 1412 | r#" |
2145 | r" | 1413 | struct Foo { a: bool } |
2146 | struct Foo { | 1414 | fn main(f: Foo) { |
2147 | a: bool, | 1415 | match f { Foo { a: true } => () } |
2148 | } | 1416 | } |
2149 | fn test_fn(f: Foo) { | 1417 | "#, |
2150 | match f { | 1418 | ); |
2151 | Foo { a: true } => {}, | 1419 | } |
2152 | } | ||
2153 | } | ||
2154 | ", | ||
2155 | ); | ||
2156 | } | 1420 | } |
2157 | } | 1421 | } |
diff --git a/crates/ra_hir_ty/src/unsafe_validation.rs b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs index c512c4f8e..9e4ed9a8b 100644 --- a/crates/ra_hir_ty/src/unsafe_validation.rs +++ b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs | |||
@@ -6,7 +6,7 @@ use std::sync::Arc; | |||
6 | use hir_def::{ | 6 | use hir_def::{ |
7 | body::Body, | 7 | body::Body, |
8 | expr::{Expr, ExprId, UnaryOp}, | 8 | expr::{Expr, ExprId, UnaryOp}, |
9 | DefWithBodyId, FunctionId, | 9 | DefWithBodyId, |
10 | }; | 10 | }; |
11 | use hir_expand::diagnostics::DiagnosticSink; | 11 | use hir_expand::diagnostics::DiagnosticSink; |
12 | 12 | ||
@@ -15,26 +15,29 @@ use crate::{ | |||
15 | InferenceResult, Ty, TypeCtor, | 15 | InferenceResult, Ty, TypeCtor, |
16 | }; | 16 | }; |
17 | 17 | ||
18 | pub struct UnsafeValidator<'a, 'b: 'a> { | 18 | pub(super) struct UnsafeValidator<'a, 'b: 'a> { |
19 | func: FunctionId, | 19 | owner: DefWithBodyId, |
20 | infer: Arc<InferenceResult>, | 20 | infer: Arc<InferenceResult>, |
21 | sink: &'a mut DiagnosticSink<'b>, | 21 | sink: &'a mut DiagnosticSink<'b>, |
22 | } | 22 | } |
23 | 23 | ||
24 | impl<'a, 'b> UnsafeValidator<'a, 'b> { | 24 | impl<'a, 'b> UnsafeValidator<'a, 'b> { |
25 | pub fn new( | 25 | pub(super) fn new( |
26 | func: FunctionId, | 26 | owner: DefWithBodyId, |
27 | infer: Arc<InferenceResult>, | 27 | infer: Arc<InferenceResult>, |
28 | sink: &'a mut DiagnosticSink<'b>, | 28 | sink: &'a mut DiagnosticSink<'b>, |
29 | ) -> UnsafeValidator<'a, 'b> { | 29 | ) -> UnsafeValidator<'a, 'b> { |
30 | UnsafeValidator { func, infer, sink } | 30 | UnsafeValidator { owner, infer, sink } |
31 | } | 31 | } |
32 | 32 | ||
33 | pub fn validate_body(&mut self, db: &dyn HirDatabase) { | 33 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { |
34 | let def = self.func.into(); | 34 | let def = self.owner.into(); |
35 | let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); | 35 | let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); |
36 | let func_data = db.function_data(self.func); | 36 | let is_unsafe = match self.owner { |
37 | if func_data.is_unsafe | 37 | DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe, |
38 | DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false, | ||
39 | }; | ||
40 | if is_unsafe | ||
38 | || unsafe_expressions | 41 | || unsafe_expressions |
39 | .iter() | 42 | .iter() |
40 | .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block) | 43 | .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block) |
@@ -118,3 +121,53 @@ fn walk_unsafe( | |||
118 | walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block); | 121 | walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block); |
119 | }); | 122 | }); |
120 | } | 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/display.rs b/crates/ra_hir_ty/src/display.rs index ac68c5661..c860c254c 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs | |||
@@ -243,10 +243,17 @@ impl HirDisplay for ApplicationTy { | |||
243 | write!(f, ")")?; | 243 | write!(f, ")")?; |
244 | } | 244 | } |
245 | } | 245 | } |
246 | TypeCtor::FnPtr { .. } => { | 246 | TypeCtor::FnPtr { is_varargs, .. } => { |
247 | let sig = FnSig::from_fn_ptr_substs(&self.parameters); | 247 | let sig = FnSig::from_fn_ptr_substs(&self.parameters, is_varargs); |
248 | write!(f, "fn(")?; | 248 | write!(f, "fn(")?; |
249 | f.write_joined(sig.params(), ", ")?; | 249 | f.write_joined(sig.params(), ", ")?; |
250 | if is_varargs { | ||
251 | if sig.params().is_empty() { | ||
252 | write!(f, "...")?; | ||
253 | } else { | ||
254 | write!(f, ", ...")?; | ||
255 | } | ||
256 | } | ||
250 | write!(f, ")")?; | 257 | write!(f, ")")?; |
251 | let ret = sig.ret(); | 258 | let ret = sig.ret(); |
252 | if *ret != Ty::unit() { | 259 | if *ret != Ty::unit() { |
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 2ce4f65cc..28f32a0a4 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -168,7 +168,7 @@ impl InferenceResult { | |||
168 | pub fn add_diagnostics( | 168 | pub fn add_diagnostics( |
169 | &self, | 169 | &self, |
170 | db: &dyn HirDatabase, | 170 | db: &dyn HirDatabase, |
171 | owner: FunctionId, | 171 | owner: DefWithBodyId, |
172 | sink: &mut DiagnosticSink, | 172 | sink: &mut DiagnosticSink, |
173 | ) { | 173 | ) { |
174 | self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) | 174 | self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) |
@@ -760,7 +760,7 @@ impl std::ops::BitOrAssign for Diverges { | |||
760 | } | 760 | } |
761 | 761 | ||
762 | mod diagnostics { | 762 | mod diagnostics { |
763 | use hir_def::{expr::ExprId, FunctionId}; | 763 | use hir_def::{expr::ExprId, DefWithBodyId}; |
764 | use hir_expand::diagnostics::DiagnosticSink; | 764 | use hir_expand::diagnostics::DiagnosticSink; |
765 | 765 | ||
766 | use crate::{ | 766 | use crate::{ |
@@ -778,17 +778,17 @@ mod diagnostics { | |||
778 | pub(super) fn add_to( | 778 | pub(super) fn add_to( |
779 | &self, | 779 | &self, |
780 | db: &dyn HirDatabase, | 780 | db: &dyn HirDatabase, |
781 | owner: FunctionId, | 781 | owner: DefWithBodyId, |
782 | sink: &mut DiagnosticSink, | 782 | sink: &mut DiagnosticSink, |
783 | ) { | 783 | ) { |
784 | match self { | 784 | match self { |
785 | InferenceDiagnostic::NoSuchField { expr, field } => { | 785 | InferenceDiagnostic::NoSuchField { expr, field } => { |
786 | let (_, source_map) = db.body_with_source_map(owner.into()); | 786 | let (_, source_map) = db.body_with_source_map(owner); |
787 | let field = source_map.field_syntax(*expr, *field); | 787 | let field = source_map.field_syntax(*expr, *field); |
788 | sink.push(NoSuchField { file: field.file_id, field: field.value }) | 788 | sink.push(NoSuchField { file: field.file_id, field: field.value }) |
789 | } | 789 | } |
790 | InferenceDiagnostic::BreakOutsideOfLoop { expr } => { | 790 | InferenceDiagnostic::BreakOutsideOfLoop { expr } => { |
791 | let (_, source_map) = db.body_with_source_map(owner.into()); | 791 | let (_, source_map) = db.body_with_source_map(owner); |
792 | let ptr = source_map | 792 | let ptr = source_map |
793 | .expr_syntax(*expr) | 793 | .expr_syntax(*expr) |
794 | .expect("break outside of loop in synthetic syntax"); | 794 | .expect("break outside of loop in synthetic syntax"); |
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index bd9a387f5..ab586b018 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs | |||
@@ -220,7 +220,7 @@ impl<'a> InferenceContext<'a> { | |||
220 | }; | 220 | }; |
221 | sig_tys.push(ret_ty.clone()); | 221 | sig_tys.push(ret_ty.clone()); |
222 | let sig_ty = Ty::apply( | 222 | let sig_ty = Ty::apply( |
223 | TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1 }, | 223 | TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1, is_varargs: false }, |
224 | Substs(sig_tys.clone().into()), | 224 | Substs(sig_tys.clone().into()), |
225 | ); | 225 | ); |
226 | let closure_ty = | 226 | let closure_ty = |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 2652d200f..c4c24a83b 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -12,15 +12,12 @@ pub mod traits; | |||
12 | pub mod method_resolution; | 12 | pub mod method_resolution; |
13 | mod op; | 13 | mod op; |
14 | mod lower; | 14 | mod lower; |
15 | mod match_checking; | ||
16 | pub(crate) mod infer; | 15 | pub(crate) mod infer; |
17 | pub(crate) mod utils; | 16 | pub(crate) mod utils; |
18 | 17 | ||
19 | pub mod display; | 18 | pub mod display; |
20 | pub mod db; | 19 | pub mod db; |
21 | pub mod diagnostics; | 20 | pub mod diagnostics; |
22 | pub mod expr; | ||
23 | pub mod unsafe_validation; | ||
24 | 21 | ||
25 | #[cfg(test)] | 22 | #[cfg(test)] |
26 | mod tests; | 23 | mod tests; |
@@ -115,7 +112,7 @@ pub enum TypeCtor { | |||
115 | /// fn foo() -> i32 { 1 } | 112 | /// fn foo() -> i32 { 1 } |
116 | /// let bar: fn() -> i32 = foo; | 113 | /// let bar: fn() -> i32 = foo; |
117 | /// ``` | 114 | /// ``` |
118 | FnPtr { num_args: u16 }, | 115 | FnPtr { num_args: u16, is_varargs: bool }, |
119 | 116 | ||
120 | /// The never type `!`. | 117 | /// The never type `!`. |
121 | Never, | 118 | Never, |
@@ -190,7 +187,7 @@ impl TypeCtor { | |||
190 | } | 187 | } |
191 | } | 188 | } |
192 | } | 189 | } |
193 | TypeCtor::FnPtr { num_args } => num_args as usize + 1, | 190 | TypeCtor::FnPtr { num_args, is_varargs: _ } => num_args as usize + 1, |
194 | TypeCtor::Tuple { cardinality } => cardinality as usize, | 191 | TypeCtor::Tuple { cardinality } => cardinality as usize, |
195 | } | 192 | } |
196 | } | 193 | } |
@@ -670,19 +667,20 @@ pub enum TyKind { | |||
670 | #[derive(Clone, PartialEq, Eq, Debug)] | 667 | #[derive(Clone, PartialEq, Eq, Debug)] |
671 | pub struct FnSig { | 668 | pub struct FnSig { |
672 | params_and_return: Arc<[Ty]>, | 669 | params_and_return: Arc<[Ty]>, |
670 | is_varargs: bool, | ||
673 | } | 671 | } |
674 | 672 | ||
675 | /// A polymorphic function signature. | 673 | /// A polymorphic function signature. |
676 | pub type PolyFnSig = Binders<FnSig>; | 674 | pub type PolyFnSig = Binders<FnSig>; |
677 | 675 | ||
678 | impl FnSig { | 676 | impl FnSig { |
679 | pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty) -> FnSig { | 677 | pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty, is_varargs: bool) -> FnSig { |
680 | params.push(ret); | 678 | params.push(ret); |
681 | FnSig { params_and_return: params.into() } | 679 | FnSig { params_and_return: params.into(), is_varargs } |
682 | } | 680 | } |
683 | 681 | ||
684 | pub fn from_fn_ptr_substs(substs: &Substs) -> FnSig { | 682 | pub fn from_fn_ptr_substs(substs: &Substs, is_varargs: bool) -> FnSig { |
685 | FnSig { params_and_return: Arc::clone(&substs.0) } | 683 | FnSig { params_and_return: Arc::clone(&substs.0), is_varargs } |
686 | } | 684 | } |
687 | 685 | ||
688 | pub fn params(&self) -> &[Ty] { | 686 | pub fn params(&self) -> &[Ty] { |
@@ -727,7 +725,7 @@ impl Ty { | |||
727 | } | 725 | } |
728 | pub fn fn_ptr(sig: FnSig) -> Self { | 726 | pub fn fn_ptr(sig: FnSig) -> Self { |
729 | Ty::apply( | 727 | Ty::apply( |
730 | TypeCtor::FnPtr { num_args: sig.params().len() as u16 }, | 728 | TypeCtor::FnPtr { num_args: sig.params().len() as u16, is_varargs: sig.is_varargs }, |
731 | Substs(sig.params_and_return), | 729 | Substs(sig.params_and_return), |
732 | ) | 730 | ) |
733 | } | 731 | } |
@@ -824,7 +822,9 @@ impl Ty { | |||
824 | fn callable_sig(&self, db: &dyn HirDatabase) -> Option<FnSig> { | 822 | fn callable_sig(&self, db: &dyn HirDatabase) -> Option<FnSig> { |
825 | match self { | 823 | match self { |
826 | Ty::Apply(a_ty) => match a_ty.ctor { | 824 | Ty::Apply(a_ty) => match a_ty.ctor { |
827 | TypeCtor::FnPtr { .. } => Some(FnSig::from_fn_ptr_substs(&a_ty.parameters)), | 825 | TypeCtor::FnPtr { is_varargs, .. } => { |
826 | Some(FnSig::from_fn_ptr_substs(&a_ty.parameters, is_varargs)) | ||
827 | } | ||
828 | TypeCtor::FnDef(def) => { | 828 | TypeCtor::FnDef(def) => { |
829 | let sig = db.callable_item_signature(def); | 829 | let sig = db.callable_item_signature(def); |
830 | Some(sig.subst(&a_ty.parameters)) | 830 | Some(sig.subst(&a_ty.parameters)) |
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 101b8aebe..6f4398e84 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs | |||
@@ -176,9 +176,12 @@ impl Ty { | |||
176 | Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty) | 176 | Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty) |
177 | } | 177 | } |
178 | TypeRef::Placeholder => Ty::Unknown, | 178 | TypeRef::Placeholder => Ty::Unknown, |
179 | TypeRef::Fn(params) => { | 179 | TypeRef::Fn(params, is_varargs) => { |
180 | let sig = Substs(params.iter().map(|tr| Ty::from_hir(ctx, tr)).collect()); | 180 | let sig = Substs(params.iter().map(|tr| Ty::from_hir(ctx, tr)).collect()); |
181 | Ty::apply(TypeCtor::FnPtr { num_args: sig.len() as u16 - 1 }, sig) | 181 | Ty::apply( |
182 | TypeCtor::FnPtr { num_args: sig.len() as u16 - 1, is_varargs: *is_varargs }, | ||
183 | sig, | ||
184 | ) | ||
182 | } | 185 | } |
183 | TypeRef::DynTrait(bounds) => { | 186 | TypeRef::DynTrait(bounds) => { |
184 | let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); | 187 | let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); |
@@ -996,7 +999,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { | |||
996 | let ret = Ty::from_hir(&ctx_ret, &data.ret_type); | 999 | let ret = Ty::from_hir(&ctx_ret, &data.ret_type); |
997 | let generics = generics(db.upcast(), def.into()); | 1000 | let generics = generics(db.upcast(), def.into()); |
998 | let num_binders = generics.len(); | 1001 | let num_binders = generics.len(); |
999 | Binders::new(num_binders, FnSig::from_params_and_return(params, ret)) | 1002 | Binders::new(num_binders, FnSig::from_params_and_return(params, ret, data.is_varargs)) |
1000 | } | 1003 | } |
1001 | 1004 | ||
1002 | /// Build the declared type of a function. This should not need to look at the | 1005 | /// Build the declared type of a function. This should not need to look at the |
@@ -1047,7 +1050,7 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS | |||
1047 | let params = | 1050 | let params = |
1048 | fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>(); | 1051 | fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>(); |
1049 | let ret = type_for_adt(db, def.into()); | 1052 | let ret = type_for_adt(db, def.into()); |
1050 | Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value)) | 1053 | Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value, false)) |
1051 | } | 1054 | } |
1052 | 1055 | ||
1053 | /// Build the type of a tuple struct constructor. | 1056 | /// Build the type of a tuple struct constructor. |
@@ -1071,7 +1074,7 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) | |||
1071 | let params = | 1074 | let params = |
1072 | fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>(); | 1075 | fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>(); |
1073 | let ret = type_for_adt(db, def.parent.into()); | 1076 | let ret = type_for_adt(db, def.parent.into()); |
1074 | Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value)) | 1077 | Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value, false)) |
1075 | } | 1078 | } |
1076 | 1079 | ||
1077 | /// Build the type of a tuple enum variant constructor. | 1080 | /// Build the type of a tuple enum variant constructor. |
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index dc447955f..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::{db::AstDatabase, diagnostics::DiagnosticSink}; | 9 | use hir_expand::db::AstDatabase; |
10 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; | 10 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; |
11 | use ra_syntax::TextRange; | 11 | use ra_syntax::TextRange; |
12 | use rustc_hash::{FxHashMap, FxHashSet}; | 12 | use rustc_hash::{FxHashMap, FxHashSet}; |
13 | use stdx::format_to; | ||
14 | use test_utils::extract_annotations; | 13 | use test_utils::extract_annotations; |
15 | 14 | ||
16 | use crate::{ | ||
17 | db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator, | ||
18 | unsafe_validation::UnsafeValidator, | ||
19 | }; | ||
20 | |||
21 | #[salsa::database( | 15 | #[salsa::database( |
22 | ra_db::SourceDatabaseExtStorage, | 16 | ra_db::SourceDatabaseExtStorage, |
23 | ra_db::SourceDatabaseStorage, | 17 | ra_db::SourceDatabaseStorage, |
@@ -82,7 +76,7 @@ impl FileLoader for TestDB { | |||
82 | } | 76 | } |
83 | 77 | ||
84 | impl TestDB { | 78 | impl TestDB { |
85 | pub fn module_for_file(&self, file_id: FileId) -> ModuleId { | 79 | pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId { |
86 | for &krate in self.relevant_crates(file_id).iter() { | 80 | for &krate in self.relevant_crates(file_id).iter() { |
87 | let crate_def_map = self.crate_def_map(krate); | 81 | let crate_def_map = self.crate_def_map(krate); |
88 | for (local_id, data) in crate_def_map.modules.iter() { | 82 | for (local_id, data) in crate_def_map.modules.iter() { |
@@ -94,67 +88,7 @@ impl TestDB { | |||
94 | panic!("Can't find module for file") | 88 | panic!("Can't find module for file") |
95 | } | 89 | } |
96 | 90 | ||
97 | fn diag<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) { | 91 | pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> { |
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 infer = self.infer(f.into()); | ||
122 | let mut sink = DiagnosticSink::new(&mut cb); | ||
123 | infer.add_diagnostics(self, f, &mut sink); | ||
124 | let mut validator = ExprValidator::new(f, infer.clone(), &mut sink); | ||
125 | validator.validate_body(self); | ||
126 | let mut validator = UnsafeValidator::new(f, infer, &mut sink); | ||
127 | validator.validate_body(self); | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | |||
132 | pub fn diagnostics(&self) -> (String, u32) { | ||
133 | let mut buf = String::new(); | ||
134 | let mut count = 0; | ||
135 | self.diag(|d| { | ||
136 | format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); | ||
137 | count += 1; | ||
138 | }); | ||
139 | (buf, count) | ||
140 | } | ||
141 | |||
142 | /// Like `diagnostics`, but filtered for a single diagnostic. | ||
143 | pub fn diagnostic<D: Diagnostic>(&self) -> (String, u32) { | ||
144 | let mut buf = String::new(); | ||
145 | let mut count = 0; | ||
146 | self.diag(|d| { | ||
147 | // We want to filter diagnostics by the particular one we are testing for, to | ||
148 | // avoid surprising results in tests. | ||
149 | if d.downcast_ref::<D>().is_some() { | ||
150 | format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); | ||
151 | count += 1; | ||
152 | }; | ||
153 | }); | ||
154 | (buf, count) | ||
155 | } | ||
156 | |||
157 | pub fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> { | ||
158 | let mut files = Vec::new(); | 92 | let mut files = Vec::new(); |
159 | let crate_graph = self.crate_graph(); | 93 | let crate_graph = self.crate_graph(); |
160 | for krate in crate_graph.iter() { | 94 | for krate in crate_graph.iter() { |
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 | } | ||
diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs index 86e22e459..60cc9a9f5 100644 --- a/crates/ra_hir_ty/src/traits/builtin.rs +++ b/crates/ra_hir_ty/src/traits/builtin.rs | |||
@@ -121,7 +121,7 @@ fn closure_fn_trait_impl_datum( | |||
121 | .build(), | 121 | .build(), |
122 | ); | 122 | ); |
123 | let sig_ty = Ty::apply( | 123 | let sig_ty = Ty::apply( |
124 | TypeCtor::FnPtr { num_args }, | 124 | TypeCtor::FnPtr { num_args, is_varargs: false }, |
125 | Substs::builder(num_args as usize + 1) | 125 | Substs::builder(num_args as usize + 1) |
126 | .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0) | 126 | .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0) |
127 | .build(), | 127 | .build(), |
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs index 06453ef82..3ebb55f77 100644 --- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs +++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs | |||
@@ -30,7 +30,8 @@ impl ToChalk for Ty { | |||
30 | Ty::Apply(apply_ty) => match apply_ty.ctor { | 30 | Ty::Apply(apply_ty) => match apply_ty.ctor { |
31 | TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters), | 31 | TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters), |
32 | TypeCtor::Array => array_to_chalk(db, apply_ty.parameters), | 32 | TypeCtor::Array => array_to_chalk(db, apply_ty.parameters), |
33 | TypeCtor::FnPtr { num_args: _ } => { | 33 | TypeCtor::FnPtr { num_args: _, is_varargs: _ } => { |
34 | // FIXME: handle is_varargs | ||
34 | let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner); | 35 | let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner); |
35 | chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: 0, substitution }) | 36 | chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: 0, substitution }) |
36 | .intern(&Interner) | 37 | .intern(&Interner) |
@@ -124,7 +125,10 @@ impl ToChalk for Ty { | |||
124 | substitution.shifted_out(&Interner).expect("fn ptr should have no binders"), | 125 | substitution.shifted_out(&Interner).expect("fn ptr should have no binders"), |
125 | ); | 126 | ); |
126 | Ty::Apply(ApplicationTy { | 127 | Ty::Apply(ApplicationTy { |
127 | ctor: TypeCtor::FnPtr { num_args: (parameters.len() - 1) as u16 }, | 128 | ctor: TypeCtor::FnPtr { |
129 | num_args: (parameters.len() - 1) as u16, | ||
130 | is_varargs: false, | ||
131 | }, | ||
128 | parameters, | 132 | parameters, |
129 | }) | 133 | }) |
130 | } | 134 | } |