aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src/diagnostics
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_ty/src/diagnostics')
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs279
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check/case_conv.rs34
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs96
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs37
-rw-r--r--crates/hir_ty/src/diagnostics/unsafe_check.rs16
5 files changed, 234 insertions, 228 deletions
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index eaeb6899f..6773ddea3 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -23,6 +23,7 @@ use hir_expand::{
23 diagnostics::DiagnosticSink, 23 diagnostics::DiagnosticSink,
24 name::{AsName, Name}, 24 name::{AsName, Name},
25}; 25};
26use stdx::{always, never};
26use syntax::{ 27use syntax::{
27 ast::{self, NameOwner}, 28 ast::{self, NameOwner},
28 AstNode, AstPtr, 29 AstNode, AstPtr,
@@ -31,7 +32,7 @@ use test_utils::mark;
31 32
32use crate::{ 33use crate::{
33 db::HirDatabase, 34 db::HirDatabase,
34 diagnostics::{decl_check::case_conv::*, CaseType, IncorrectCase}, 35 diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase},
35}; 36};
36 37
37mod allow { 38mod allow {
@@ -40,7 +41,7 @@ mod allow {
40 pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; 41 pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types";
41} 42}
42 43
43pub(super) struct DeclValidator<'a, 'b: 'a> { 44pub(super) struct DeclValidator<'a, 'b> {
44 db: &'a dyn HirDatabase, 45 db: &'a dyn HirDatabase,
45 krate: CrateId, 46 krate: CrateId,
46 sink: &'a mut DiagnosticSink<'b>, 47 sink: &'a mut DiagnosticSink<'b>,
@@ -77,7 +78,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
77 AdtId::StructId(struct_id) => self.validate_struct(struct_id), 78 AdtId::StructId(struct_id) => self.validate_struct(struct_id),
78 AdtId::EnumId(enum_id) => self.validate_enum(enum_id), 79 AdtId::EnumId(enum_id) => self.validate_enum(enum_id),
79 AdtId::UnionId(_) => { 80 AdtId::UnionId(_) => {
80 // Unions aren't yet supported by this validator. 81 // FIXME: Unions aren't yet supported by this validator.
81 } 82 }
82 } 83 }
83 } 84 }
@@ -111,63 +112,50 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
111 112
112 // Check the function name. 113 // Check the function name.
113 let function_name = data.name.to_string(); 114 let function_name = data.name.to_string();
114 let fn_name_replacement = if let Some(new_name) = to_lower_snake_case(&function_name) { 115 let fn_name_replacement = to_lower_snake_case(&function_name).map(|new_name| Replacement {
115 let replacement = Replacement { 116 current_name: data.name.clone(),
116 current_name: data.name.clone(), 117 suggested_text: new_name,
117 suggested_text: new_name, 118 expected_case: CaseType::LowerSnakeCase,
118 expected_case: CaseType::LowerSnakeCase, 119 });
119 };
120 Some(replacement)
121 } else {
122 None
123 };
124 120
125 // Check the param names. 121 // Check the param names.
126 let mut fn_param_replacements = Vec::new(); 122 let fn_param_replacements = body
127 123 .params
128 for pat_id in body.params.iter().cloned() { 124 .iter()
129 let pat = &body[pat_id]; 125 .filter_map(|&id| match &body[id] {
130 126 Pat::Bind { name, .. } => Some(name),
131 let param_name = match pat { 127 _ => None,
132 Pat::Bind { name, .. } => name, 128 })
133 _ => continue, 129 .filter_map(|param_name| {
134 }; 130 Some(Replacement {
135
136 let name = param_name.to_string();
137 if let Some(new_name) = to_lower_snake_case(&name) {
138 let replacement = Replacement {
139 current_name: param_name.clone(), 131 current_name: param_name.clone(),
140 suggested_text: new_name, 132 suggested_text: to_lower_snake_case(&param_name.to_string())?,
141 expected_case: CaseType::LowerSnakeCase, 133 expected_case: CaseType::LowerSnakeCase,
142 }; 134 })
143 fn_param_replacements.push(replacement); 135 })
144 } 136 .collect();
145 }
146 137
147 // Check the patterns inside the function body. 138 // Check the patterns inside the function body.
148 let mut pats_replacements = Vec::new(); 139 let pats_replacements = body
149 140 .pats
150 for (pat_idx, pat) in body.pats.iter() { 141 .iter()
151 if body.params.contains(&pat_idx) { 142 // We aren't interested in function parameters, we've processed them above.
152 // We aren't interested in function parameters, we've processed them above. 143 .filter(|(pat_idx, _)| !body.params.contains(&pat_idx))
153 continue; 144 .filter_map(|(id, pat)| match pat {
154 } 145 Pat::Bind { name, .. } => Some((id, name)),
155 146 _ => None,
156 let bind_name = match pat { 147 })
157 Pat::Bind { name, .. } => name, 148 .filter_map(|(id, bind_name)| {
158 _ => continue, 149 Some((
159 }; 150 id,
160 151 Replacement {
161 let name = bind_name.to_string(); 152 current_name: bind_name.clone(),
162 if let Some(new_name) = to_lower_snake_case(&name) { 153 suggested_text: to_lower_snake_case(&bind_name.to_string())?,
163 let replacement = Replacement { 154 expected_case: CaseType::LowerSnakeCase,
164 current_name: bind_name.clone(), 155 },
165 suggested_text: new_name, 156 ))
166 expected_case: CaseType::LowerSnakeCase, 157 })
167 }; 158 .collect();
168 pats_replacements.push((pat_idx, replacement));
169 }
170 }
171 159
172 // If there is at least one element to spawn a warning on, go to the source map and generate a warning. 160 // If there is at least one element to spawn a warning on, go to the source map and generate a warning.
173 self.create_incorrect_case_diagnostic_for_func( 161 self.create_incorrect_case_diagnostic_for_func(
@@ -199,8 +187,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
199 let ast_ptr = match fn_src.value.name() { 187 let ast_ptr = match fn_src.value.name() {
200 Some(name) => name, 188 Some(name) => name,
201 None => { 189 None => {
202 // We don't want rust-analyzer to panic over this, but it is definitely some kind of error in the logic. 190 never!(
203 log::error!(
204 "Replacement ({:?}) was generated for a function without a name: {:?}", 191 "Replacement ({:?}) was generated for a function without a name: {:?}",
205 replacement, 192 replacement,
206 fn_src 193 fn_src
@@ -211,7 +198,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
211 198
212 let diagnostic = IncorrectCase { 199 let diagnostic = IncorrectCase {
213 file: fn_src.file_id, 200 file: fn_src.file_id,
214 ident_type: "Function".to_string(), 201 ident_type: IdentType::Function,
215 ident: AstPtr::new(&ast_ptr).into(), 202 ident: AstPtr::new(&ast_ptr).into(),
216 expected_case: replacement.expected_case, 203 expected_case: replacement.expected_case,
217 ident_text: replacement.current_name.to_string(), 204 ident_text: replacement.current_name.to_string(),
@@ -225,12 +212,12 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
225 let fn_params_list = match fn_src.value.param_list() { 212 let fn_params_list = match fn_src.value.param_list() {
226 Some(params) => params, 213 Some(params) => params,
227 None => { 214 None => {
228 if !fn_param_replacements.is_empty() { 215 always!(
229 log::error!( 216 fn_param_replacements.is_empty(),
230 "Replacements ({:?}) were generated for a function parameters which had no parameters list: {:?}", 217 "Replacements ({:?}) were generated for a function parameters which had no parameters list: {:?}",
231 fn_param_replacements, fn_src 218 fn_param_replacements,
232 ); 219 fn_src
233 } 220 );
234 return; 221 return;
235 } 222 }
236 }; 223 };
@@ -240,23 +227,25 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
240 // actual params list, but just some of them (ones that named correctly) are skipped. 227 // actual params list, but just some of them (ones that named correctly) are skipped.
241 let ast_ptr: ast::Name = loop { 228 let ast_ptr: ast::Name = loop {
242 match fn_params_iter.next() { 229 match fn_params_iter.next() {
243 Some(element) 230 Some(element) => {
244 if pat_equals_to_name(element.pat(), &param_to_rename.current_name) => 231 if let Some(ast::Pat::IdentPat(pat)) = element.pat() {
245 { 232 if pat.to_string() == param_to_rename.current_name.to_string() {
246 if let ast::Pat::IdentPat(pat) = element.pat().unwrap() { 233 if let Some(name) = pat.name() {
247 break pat.name().unwrap(); 234 break name;
248 } else { 235 }
249 // This is critical. If we consider this parameter the expected one, 236 // This is critical. If we consider this parameter the expected one,
250 // it **must** have a name. 237 // it **must** have a name.
251 panic!( 238 never!(
252 "Pattern {:?} equals to expected replacement {:?}, but has no name", 239 "Pattern {:?} equals to expected replacement {:?}, but has no name",
253 element, param_to_rename 240 element,
254 ); 241 param_to_rename
242 );
243 return;
244 }
255 } 245 }
256 } 246 }
257 Some(_) => {}
258 None => { 247 None => {
259 log::error!( 248 never!(
260 "Replacement ({:?}) was generated for a function parameter which was not found: {:?}", 249 "Replacement ({:?}) was generated for a function parameter which was not found: {:?}",
261 param_to_rename, fn_src 250 param_to_rename, fn_src
262 ); 251 );
@@ -267,7 +256,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
267 256
268 let diagnostic = IncorrectCase { 257 let diagnostic = IncorrectCase {
269 file: fn_src.file_id, 258 file: fn_src.file_id,
270 ident_type: "Argument".to_string(), 259 ident_type: IdentType::Argument,
271 ident: AstPtr::new(&ast_ptr).into(), 260 ident: AstPtr::new(&ast_ptr).into(),
272 expected_case: param_to_rename.expected_case, 261 expected_case: param_to_rename.expected_case,
273 ident_text: param_to_rename.current_name.to_string(), 262 ident_text: param_to_rename.current_name.to_string(),
@@ -309,8 +298,8 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
309 // We have to check that it's either `let var = ...` or `var @ Variant(_)` statement, 298 // We have to check that it's either `let var = ...` or `var @ Variant(_)` statement,
310 // because e.g. match arms are patterns as well. 299 // because e.g. match arms are patterns as well.
311 // In other words, we check that it's a named variable binding. 300 // In other words, we check that it's a named variable binding.
312 let is_binding = ast::LetStmt::cast(parent.clone()).is_some() 301 let is_binding = ast::LetStmt::can_cast(parent.kind())
313 || (ast::MatchArm::cast(parent).is_some() 302 || (ast::MatchArm::can_cast(parent.kind())
314 && ident_pat.at_token().is_some()); 303 && ident_pat.at_token().is_some());
315 if !is_binding { 304 if !is_binding {
316 // This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm. 305 // This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm.
@@ -319,7 +308,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
319 308
320 let diagnostic = IncorrectCase { 309 let diagnostic = IncorrectCase {
321 file: source_ptr.file_id, 310 file: source_ptr.file_id,
322 ident_type: "Variable".to_string(), 311 ident_type: IdentType::Variable,
323 ident: AstPtr::new(&name_ast).into(), 312 ident: AstPtr::new(&name_ast).into(),
324 expected_case: replacement.expected_case, 313 expected_case: replacement.expected_case,
325 ident_text: replacement.current_name.to_string(), 314 ident_text: replacement.current_name.to_string(),
@@ -341,17 +330,12 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
341 330
342 // Check the structure name. 331 // Check the structure name.
343 let struct_name = data.name.to_string(); 332 let struct_name = data.name.to_string();
344 let struct_name_replacement = if let Some(new_name) = to_camel_case(&struct_name) { 333 let struct_name_replacement = if !non_camel_case_allowed {
345 let replacement = Replacement { 334 to_camel_case(&struct_name).map(|new_name| Replacement {
346 current_name: data.name.clone(), 335 current_name: data.name.clone(),
347 suggested_text: new_name, 336 suggested_text: new_name,
348 expected_case: CaseType::UpperCamelCase, 337 expected_case: CaseType::UpperCamelCase,
349 }; 338 })
350 if non_camel_case_allowed {
351 None
352 } else {
353 Some(replacement)
354 }
355 } else { 339 } else {
356 None 340 None
357 }; 341 };
@@ -403,8 +387,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
403 let ast_ptr = match struct_src.value.name() { 387 let ast_ptr = match struct_src.value.name() {
404 Some(name) => name, 388 Some(name) => name,
405 None => { 389 None => {
406 // We don't want rust-analyzer to panic over this, but it is definitely some kind of error in the logic. 390 never!(
407 log::error!(
408 "Replacement ({:?}) was generated for a structure without a name: {:?}", 391 "Replacement ({:?}) was generated for a structure without a name: {:?}",
409 replacement, 392 replacement,
410 struct_src 393 struct_src
@@ -415,7 +398,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
415 398
416 let diagnostic = IncorrectCase { 399 let diagnostic = IncorrectCase {
417 file: struct_src.file_id, 400 file: struct_src.file_id,
418 ident_type: "Structure".to_string(), 401 ident_type: IdentType::Structure,
419 ident: AstPtr::new(&ast_ptr).into(), 402 ident: AstPtr::new(&ast_ptr).into(),
420 expected_case: replacement.expected_case, 403 expected_case: replacement.expected_case,
421 ident_text: replacement.current_name.to_string(), 404 ident_text: replacement.current_name.to_string(),
@@ -428,12 +411,12 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
428 let struct_fields_list = match struct_src.value.field_list() { 411 let struct_fields_list = match struct_src.value.field_list() {
429 Some(ast::FieldList::RecordFieldList(fields)) => fields, 412 Some(ast::FieldList::RecordFieldList(fields)) => fields,
430 _ => { 413 _ => {
431 if !struct_fields_replacements.is_empty() { 414 always!(
432 log::error!( 415 struct_fields_replacements.is_empty(),
433 "Replacements ({:?}) were generated for a structure fields which had no fields list: {:?}", 416 "Replacements ({:?}) were generated for a structure fields which had no fields list: {:?}",
434 struct_fields_replacements, struct_src 417 struct_fields_replacements,
435 ); 418 struct_src
436 } 419 );
437 return; 420 return;
438 } 421 }
439 }; 422 };
@@ -442,13 +425,14 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
442 // We assume that parameters in replacement are in the same order as in the 425 // We assume that parameters in replacement are in the same order as in the
443 // actual params list, but just some of them (ones that named correctly) are skipped. 426 // actual params list, but just some of them (ones that named correctly) are skipped.
444 let ast_ptr = loop { 427 let ast_ptr = loop {
445 match struct_fields_iter.next() { 428 match struct_fields_iter.next().and_then(|field| field.name()) {
446 Some(element) if names_equal(element.name(), &field_to_rename.current_name) => { 429 Some(field_name) => {
447 break element.name().unwrap() 430 if field_name.as_name() == field_to_rename.current_name {
431 break field_name;
432 }
448 } 433 }
449 Some(_) => {}
450 None => { 434 None => {
451 log::error!( 435 never!(
452 "Replacement ({:?}) was generated for a structure field which was not found: {:?}", 436 "Replacement ({:?}) was generated for a structure field which was not found: {:?}",
453 field_to_rename, struct_src 437 field_to_rename, struct_src
454 ); 438 );
@@ -459,7 +443,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
459 443
460 let diagnostic = IncorrectCase { 444 let diagnostic = IncorrectCase {
461 file: struct_src.file_id, 445 file: struct_src.file_id,
462 ident_type: "Field".to_string(), 446 ident_type: IdentType::Field,
463 ident: AstPtr::new(&ast_ptr).into(), 447 ident: AstPtr::new(&ast_ptr).into(),
464 expected_case: field_to_rename.expected_case, 448 expected_case: field_to_rename.expected_case,
465 ident_text: field_to_rename.current_name.to_string(), 449 ident_text: field_to_rename.current_name.to_string(),
@@ -480,31 +464,24 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
480 464
481 // Check the enum name. 465 // Check the enum name.
482 let enum_name = data.name.to_string(); 466 let enum_name = data.name.to_string();
483 let enum_name_replacement = if let Some(new_name) = to_camel_case(&enum_name) { 467 let enum_name_replacement = to_camel_case(&enum_name).map(|new_name| Replacement {
484 let replacement = Replacement { 468 current_name: data.name.clone(),
485 current_name: data.name.clone(), 469 suggested_text: new_name,
486 suggested_text: new_name, 470 expected_case: CaseType::UpperCamelCase,
487 expected_case: CaseType::UpperCamelCase, 471 });
488 };
489 Some(replacement)
490 } else {
491 None
492 };
493 472
494 // Check the field names. 473 // Check the field names.
495 let mut enum_fields_replacements = Vec::new(); 474 let enum_fields_replacements = data
496 475 .variants
497 for (_, variant) in data.variants.iter() { 476 .iter()
498 let variant_name = variant.name.to_string(); 477 .filter_map(|(_, variant)| {
499 if let Some(new_name) = to_camel_case(&variant_name) { 478 Some(Replacement {
500 let replacement = Replacement {
501 current_name: variant.name.clone(), 479 current_name: variant.name.clone(),
502 suggested_text: new_name, 480 suggested_text: to_camel_case(&variant.name.to_string())?,
503 expected_case: CaseType::UpperCamelCase, 481 expected_case: CaseType::UpperCamelCase,
504 }; 482 })
505 enum_fields_replacements.push(replacement); 483 })
506 } 484 .collect();
507 }
508 485
509 // If there is at least one element to spawn a warning on, go to the source map and generate a warning. 486 // If there is at least one element to spawn a warning on, go to the source map and generate a warning.
510 self.create_incorrect_case_diagnostic_for_enum( 487 self.create_incorrect_case_diagnostic_for_enum(
@@ -534,8 +511,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
534 let ast_ptr = match enum_src.value.name() { 511 let ast_ptr = match enum_src.value.name() {
535 Some(name) => name, 512 Some(name) => name,
536 None => { 513 None => {
537 // We don't want rust-analyzer to panic over this, but it is definitely some kind of error in the logic. 514 never!(
538 log::error!(
539 "Replacement ({:?}) was generated for a enum without a name: {:?}", 515 "Replacement ({:?}) was generated for a enum without a name: {:?}",
540 replacement, 516 replacement,
541 enum_src 517 enum_src
@@ -546,7 +522,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
546 522
547 let diagnostic = IncorrectCase { 523 let diagnostic = IncorrectCase {
548 file: enum_src.file_id, 524 file: enum_src.file_id,
549 ident_type: "Enum".to_string(), 525 ident_type: IdentType::Enum,
550 ident: AstPtr::new(&ast_ptr).into(), 526 ident: AstPtr::new(&ast_ptr).into(),
551 expected_case: replacement.expected_case, 527 expected_case: replacement.expected_case,
552 ident_text: replacement.current_name.to_string(), 528 ident_text: replacement.current_name.to_string(),
@@ -559,12 +535,12 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
559 let enum_variants_list = match enum_src.value.variant_list() { 535 let enum_variants_list = match enum_src.value.variant_list() {
560 Some(variants) => variants, 536 Some(variants) => variants,
561 _ => { 537 _ => {
562 if !enum_variants_replacements.is_empty() { 538 always!(
563 log::error!( 539 enum_variants_replacements.is_empty(),
564 "Replacements ({:?}) were generated for a enum variants which had no fields list: {:?}", 540 "Replacements ({:?}) were generated for a enum variants which had no fields list: {:?}",
565 enum_variants_replacements, enum_src 541 enum_variants_replacements,
566 ); 542 enum_src
567 } 543 );
568 return; 544 return;
569 } 545 }
570 }; 546 };
@@ -573,15 +549,14 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
573 // We assume that parameters in replacement are in the same order as in the 549 // We assume that parameters in replacement are in the same order as in the
574 // actual params list, but just some of them (ones that named correctly) are skipped. 550 // actual params list, but just some of them (ones that named correctly) are skipped.
575 let ast_ptr = loop { 551 let ast_ptr = loop {
576 match enum_variants_iter.next() { 552 match enum_variants_iter.next().and_then(|v| v.name()) {
577 Some(variant) 553 Some(variant_name) => {
578 if names_equal(variant.name(), &variant_to_rename.current_name) => 554 if variant_name.as_name() == variant_to_rename.current_name {
579 { 555 break variant_name;
580 break variant.name().unwrap() 556 }
581 } 557 }
582 Some(_) => {}
583 None => { 558 None => {
584 log::error!( 559 never!(
585 "Replacement ({:?}) was generated for a enum variant which was not found: {:?}", 560 "Replacement ({:?}) was generated for a enum variant which was not found: {:?}",
586 variant_to_rename, enum_src 561 variant_to_rename, enum_src
587 ); 562 );
@@ -592,7 +567,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
592 567
593 let diagnostic = IncorrectCase { 568 let diagnostic = IncorrectCase {
594 file: enum_src.file_id, 569 file: enum_src.file_id,
595 ident_type: "Variant".to_string(), 570 ident_type: IdentType::Variant,
596 ident: AstPtr::new(&ast_ptr).into(), 571 ident: AstPtr::new(&ast_ptr).into(),
597 expected_case: variant_to_rename.expected_case, 572 expected_case: variant_to_rename.expected_case,
598 ident_text: variant_to_rename.current_name.to_string(), 573 ident_text: variant_to_rename.current_name.to_string(),
@@ -637,7 +612,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
637 612
638 let diagnostic = IncorrectCase { 613 let diagnostic = IncorrectCase {
639 file: const_src.file_id, 614 file: const_src.file_id,
640 ident_type: "Constant".to_string(), 615 ident_type: IdentType::Constant,
641 ident: AstPtr::new(&ast_ptr).into(), 616 ident: AstPtr::new(&ast_ptr).into(),
642 expected_case: replacement.expected_case, 617 expected_case: replacement.expected_case,
643 ident_text: replacement.current_name.to_string(), 618 ident_text: replacement.current_name.to_string(),
@@ -685,7 +660,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
685 660
686 let diagnostic = IncorrectCase { 661 let diagnostic = IncorrectCase {
687 file: static_src.file_id, 662 file: static_src.file_id,
688 ident_type: "Static variable".to_string(), 663 ident_type: IdentType::StaticVariable,
689 ident: AstPtr::new(&ast_ptr).into(), 664 ident: AstPtr::new(&ast_ptr).into(),
690 expected_case: replacement.expected_case, 665 expected_case: replacement.expected_case,
691 ident_text: replacement.current_name.to_string(), 666 ident_text: replacement.current_name.to_string(),
@@ -696,22 +671,6 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
696 } 671 }
697} 672}
698 673
699fn names_equal(left: Option<ast::Name>, right: &Name) -> bool {
700 if let Some(left) = left {
701 &left.as_name() == right
702 } else {
703 false
704 }
705}
706
707fn pat_equals_to_name(pat: Option<ast::Pat>, name: &Name) -> bool {
708 if let Some(ast::Pat::IdentPat(ident)) = pat {
709 ident.to_string() == name.to_string()
710 } else {
711 false
712 }
713}
714
715#[cfg(test)] 674#[cfg(test)]
716mod tests { 675mod tests {
717 use test_utils::mark; 676 use test_utils::mark;
diff --git a/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs b/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs
index 14e4d92f0..3ab36caf2 100644
--- a/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs
@@ -5,7 +5,7 @@
5// from file /compiler/rustc_lint/src/nonstandard_style.rs 5// from file /compiler/rustc_lint/src/nonstandard_style.rs
6 6
7/// Converts an identifier to an UpperCamelCase form. 7/// Converts an identifier to an UpperCamelCase form.
8/// Returns `None` if the string is already is UpperCamelCase. 8/// Returns `None` if the string is already in UpperCamelCase.
9pub(crate) fn to_camel_case(ident: &str) -> Option<String> { 9pub(crate) fn to_camel_case(ident: &str) -> Option<String> {
10 if is_camel_case(ident) { 10 if is_camel_case(ident) {
11 return None; 11 return None;
@@ -17,7 +17,7 @@ pub(crate) fn to_camel_case(ident: &str) -> Option<String> {
17 .split('_') 17 .split('_')
18 .filter(|component| !component.is_empty()) 18 .filter(|component| !component.is_empty())
19 .map(|component| { 19 .map(|component| {
20 let mut camel_cased_component = String::new(); 20 let mut camel_cased_component = String::with_capacity(component.len());
21 21
22 let mut new_word = true; 22 let mut new_word = true;
23 let mut prev_is_lower_case = true; 23 let mut prev_is_lower_case = true;
@@ -30,9 +30,9 @@ pub(crate) fn to_camel_case(ident: &str) -> Option<String> {
30 } 30 }
31 31
32 if new_word { 32 if new_word {
33 camel_cased_component.push_str(&c.to_uppercase().to_string()); 33 camel_cased_component.extend(c.to_uppercase());
34 } else { 34 } else {
35 camel_cased_component.push_str(&c.to_lowercase().to_string()); 35 camel_cased_component.extend(c.to_lowercase());
36 } 36 }
37 37
38 prev_is_lower_case = c.is_lowercase(); 38 prev_is_lower_case = c.is_lowercase();
@@ -41,16 +41,16 @@ pub(crate) fn to_camel_case(ident: &str) -> Option<String> {
41 41
42 camel_cased_component 42 camel_cased_component
43 }) 43 })
44 .fold((String::new(), None), |(acc, prev): (String, Option<String>), next| { 44 .fold((String::new(), None), |(acc, prev): (_, Option<String>), next| {
45 // separate two components with an underscore if their boundary cannot 45 // separate two components with an underscore if their boundary cannot
46 // be distinguished using a uppercase/lowercase case distinction 46 // be distinguished using a uppercase/lowercase case distinction
47 let join = if let Some(prev) = prev { 47 let join = prev
48 let l = prev.chars().last().unwrap(); 48 .and_then(|prev| {
49 let f = next.chars().next().unwrap(); 49 let f = next.chars().next()?;
50 !char_has_case(l) && !char_has_case(f) 50 let l = prev.chars().last()?;
51 } else { 51 Some(!char_has_case(l) && !char_has_case(f))
52 false 52 })
53 }; 53 .unwrap_or(false);
54 (acc + if join { "_" } else { "" } + &next, Some(next)) 54 (acc + if join { "_" } else { "" } + &next, Some(next))
55 }) 55 })
56 .0; 56 .0;
@@ -92,14 +92,12 @@ fn is_camel_case(name: &str) -> bool {
92 let mut fst = None; 92 let mut fst = None;
93 // start with a non-lowercase letter rather than non-uppercase 93 // start with a non-lowercase letter rather than non-uppercase
94 // ones (some scripts don't have a concept of upper/lowercase) 94 // ones (some scripts don't have a concept of upper/lowercase)
95 !name.chars().next().unwrap().is_lowercase() 95 name.chars().next().map_or(true, |c| !c.is_lowercase())
96 && !name.contains("__") 96 && !name.contains("__")
97 && !name.chars().any(|snd| { 97 && !name.chars().any(|snd| {
98 let ret = match (fst, snd) { 98 let ret = match fst {
99 (None, _) => false, 99 None => false,
100 (Some(fst), snd) => { 100 Some(fst) => char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_',
101 char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_'
102 }
103 }; 101 };
104 fst = Some(snd); 102 fst = Some(snd);
105 103
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 107417c27..66a88e2b6 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -2,8 +2,10 @@
2 2
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_def::{expr::Statement, path::path, resolver::HasResolver, AdtId, DefWithBodyId}; 5use hir_def::{
6use hir_expand::diagnostics::DiagnosticSink; 6 expr::Statement, path::path, resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId,
7};
8use hir_expand::{diagnostics::DiagnosticSink, name};
7use rustc_hash::FxHashSet; 9use rustc_hash::FxHashSet;
8use syntax::{ast, AstPtr}; 10use syntax::{ast, AstPtr};
9 11
@@ -15,7 +17,7 @@ use crate::{
15 MissingPatFields, RemoveThisSemicolon, 17 MissingPatFields, RemoveThisSemicolon,
16 }, 18 },
17 utils::variant_data, 19 utils::variant_data,
18 ApplicationTy, InferenceResult, Ty, TypeCtor, 20 InferenceResult, Ty,
19}; 21};
20 22
21pub(crate) use hir_def::{ 23pub(crate) use hir_def::{
@@ -24,6 +26,8 @@ pub(crate) use hir_def::{
24 LocalFieldId, VariantId, 26 LocalFieldId, VariantId,
25}; 27};
26 28
29use super::ReplaceFilterMapNextWithFindMap;
30
27pub(super) struct ExprValidator<'a, 'b: 'a> { 31pub(super) struct ExprValidator<'a, 'b: 'a> {
28 owner: DefWithBodyId, 32 owner: DefWithBodyId,
29 infer: Arc<InferenceResult>, 33 infer: Arc<InferenceResult>,
@@ -40,6 +44,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
40 } 44 }
41 45
42 pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { 46 pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) {
47 self.check_for_filter_map_next(db);
48
43 let body = db.body(self.owner.into()); 49 let body = db.body(self.owner.into());
44 50
45 for (id, expr) in body.exprs.iter() { 51 for (id, expr) in body.exprs.iter() {
@@ -150,20 +156,76 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
150 } 156 }
151 } 157 }
152 158
153 fn validate_call(&mut self, db: &dyn HirDatabase, call_id: ExprId, expr: &Expr) -> Option<()> { 159 fn check_for_filter_map_next(&mut self, db: &dyn HirDatabase) {
160 // Find the FunctionIds for Iterator::filter_map and Iterator::next
161 let iterator_path = path![core::iter::Iterator];
162 let resolver = self.owner.resolver(db.upcast());
163 let iterator_trait_id = match resolver.resolve_known_trait(db.upcast(), &iterator_path) {
164 Some(id) => id,
165 None => return,
166 };
167 let iterator_trait_items = &db.trait_data(iterator_trait_id).items;
168 let filter_map_function_id =
169 match iterator_trait_items.iter().find(|item| item.0 == name![filter_map]) {
170 Some((_, AssocItemId::FunctionId(id))) => id,
171 _ => return,
172 };
173 let next_function_id = match iterator_trait_items.iter().find(|item| item.0 == name![next])
174 {
175 Some((_, AssocItemId::FunctionId(id))) => id,
176 _ => return,
177 };
178
179 // Search function body for instances of .filter_map(..).next()
180 let body = db.body(self.owner.into());
181 let mut prev = None;
182 for (id, expr) in body.exprs.iter() {
183 if let Expr::MethodCall { receiver, .. } = expr {
184 let function_id = match self.infer.method_resolution(id) {
185 Some(id) => id,
186 None => continue,
187 };
188
189 if function_id == *filter_map_function_id {
190 prev = Some(id);
191 continue;
192 }
193
194 if function_id == *next_function_id {
195 if let Some(filter_map_id) = prev {
196 if *receiver == filter_map_id {
197 let (_, source_map) = db.body_with_source_map(self.owner.into());
198 if let Ok(next_source_ptr) = source_map.expr_syntax(id) {
199 self.sink.push(ReplaceFilterMapNextWithFindMap {
200 file: next_source_ptr.file_id,
201 next_expr: next_source_ptr.value,
202 });
203 }
204 }
205 }
206 }
207 }
208 prev = None;
209 }
210 }
211
212 fn validate_call(&mut self, db: &dyn HirDatabase, call_id: ExprId, expr: &Expr) {
154 // Check that the number of arguments matches the number of parameters. 213 // Check that the number of arguments matches the number of parameters.
155 214
156 // FIXME: Due to shortcomings in the current type system implementation, only emit this 215 // FIXME: Due to shortcomings in the current type system implementation, only emit this
157 // diagnostic if there are no type mismatches in the containing function. 216 // diagnostic if there are no type mismatches in the containing function.
158 if self.infer.type_mismatches.iter().next().is_some() { 217 if self.infer.type_mismatches.iter().next().is_some() {
159 return None; 218 return;
160 } 219 }
161 220
162 let is_method_call = matches!(expr, Expr::MethodCall { .. }); 221 let is_method_call = matches!(expr, Expr::MethodCall { .. });
163 let (sig, args) = match expr { 222 let (sig, args) = match expr {
164 Expr::Call { callee, args } => { 223 Expr::Call { callee, args } => {
165 let callee = &self.infer.type_of_expr[*callee]; 224 let callee = &self.infer.type_of_expr[*callee];
166 let sig = callee.callable_sig(db)?; 225 let sig = match callee.callable_sig(db) {
226 Some(sig) => sig,
227 None => return,
228 };
167 (sig, args.clone()) 229 (sig, args.clone())
168 } 230 }
169 Expr::MethodCall { receiver, args, .. } => { 231 Expr::MethodCall { receiver, args, .. } => {
@@ -175,22 +237,25 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
175 // if the receiver is of unknown type, it's very likely we 237 // if the receiver is of unknown type, it's very likely we
176 // don't know enough to correctly resolve the method call. 238 // don't know enough to correctly resolve the method call.
177 // This is kind of a band-aid for #6975. 239 // This is kind of a band-aid for #6975.
178 return None; 240 return;
179 } 241 }
180 242
181 // FIXME: note that we erase information about substs here. This 243 // FIXME: note that we erase information about substs here. This
182 // is not right, but, luckily, doesn't matter as we care only 244 // is not right, but, luckily, doesn't matter as we care only
183 // about the number of params 245 // about the number of params
184 let callee = self.infer.method_resolution(call_id)?; 246 let callee = match self.infer.method_resolution(call_id) {
247 Some(callee) => callee,
248 None => return,
249 };
185 let sig = db.callable_item_signature(callee.into()).value; 250 let sig = db.callable_item_signature(callee.into()).value;
186 251
187 (sig, args) 252 (sig, args)
188 } 253 }
189 _ => return None, 254 _ => return,
190 }; 255 };
191 256
192 if sig.is_varargs { 257 if sig.is_varargs {
193 return None; 258 return;
194 } 259 }
195 260
196 let params = sig.params(); 261 let params = sig.params();
@@ -213,8 +278,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
213 }); 278 });
214 } 279 }
215 } 280 }
216
217 None
218 } 281 }
219 282
220 fn validate_match( 283 fn validate_match(
@@ -318,14 +381,11 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
318 _ => return, 381 _ => return,
319 }; 382 };
320 383
321 let core_result_ctor = TypeCtor::Adt(AdtId::EnumId(core_result_enum)); 384 let (params, required) = match mismatch.expected {
322 let core_option_ctor = TypeCtor::Adt(AdtId::EnumId(core_option_enum)); 385 Ty::Adt(AdtId::EnumId(enum_id), ref parameters) if enum_id == core_result_enum => {
323
324 let (params, required) = match &mismatch.expected {
325 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_result_ctor => {
326 (parameters, "Ok".to_string()) 386 (parameters, "Ok".to_string())
327 } 387 }
328 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_option_ctor => { 388 Ty::Adt(AdtId::EnumId(enum_id), ref parameters) if enum_id == core_option_enum => {
329 (parameters, "Some".to_string()) 389 (parameters, "Some".to_string())
330 } 390 }
331 _ => return, 391 _ => return,
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index fbe760c39..86fee0050 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -227,7 +227,7 @@ use hir_def::{
227use la_arena::Idx; 227use la_arena::Idx;
228use smallvec::{smallvec, SmallVec}; 228use smallvec::{smallvec, SmallVec};
229 229
230use crate::{db::HirDatabase, ApplicationTy, InferenceResult, Ty, TypeCtor}; 230use crate::{db::HirDatabase, InferenceResult, Ty};
231 231
232#[derive(Debug, Clone, Copy)] 232#[derive(Debug, Clone, Copy)]
233/// Either a pattern from the source code being analyzed, represented as 233/// Either a pattern from the source code being analyzed, represented as
@@ -627,14 +627,12 @@ pub(super) fn is_useful(
627 // - `!` type 627 // - `!` type
628 // In those cases, no match arm is useful. 628 // In those cases, no match arm is useful.
629 match cx.infer[cx.match_expr].strip_references() { 629 match cx.infer[cx.match_expr].strip_references() {
630 Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(AdtId::EnumId(enum_id)), .. }) => { 630 Ty::Adt(AdtId::EnumId(enum_id), ..) => {
631 if cx.db.enum_data(*enum_id).variants.is_empty() { 631 if cx.db.enum_data(*enum_id).variants.is_empty() {
632 return Ok(Usefulness::NotUseful); 632 return Ok(Usefulness::NotUseful);
633 } 633 }
634 } 634 }
635 Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) => { 635 Ty::Never => return Ok(Usefulness::NotUseful),
636 return Ok(Usefulness::NotUseful);
637 }
638 _ => (), 636 _ => (),
639 } 637 }
640 638
@@ -1495,6 +1493,20 @@ fn main(f: Foo) {
1495 ); 1493 );
1496 } 1494 }
1497 1495
1496 #[test]
1497 fn internal_or() {
1498 check_diagnostics(
1499 r#"
1500fn main() {
1501 enum Either { A(bool), B }
1502 match Either::B {
1503 //^^^^^^^^^ Missing match arm
1504 Either::A(true | false) => (),
1505 }
1506}
1507"#,
1508 );
1509 }
1498 mod false_negatives { 1510 mod false_negatives {
1499 //! The implementation of match checking here is a work in progress. As we roll this out, we 1511 //! The implementation of match checking here is a work in progress. As we roll this out, we
1500 //! prefer false negatives to false positives (ideally there would be no false positives). This 1512 //! prefer false negatives to false positives (ideally there would be no false positives). This
@@ -1521,20 +1533,5 @@ fn main() {
1521"#, 1533"#,
1522 ); 1534 );
1523 } 1535 }
1524
1525 #[test]
1526 fn internal_or() {
1527 // We do not currently handle patterns with internal `or`s.
1528 check_diagnostics(
1529 r#"
1530fn main() {
1531 enum Either { A(bool), B }
1532 match Either::B {
1533 Either::A(true | false) => (),
1534 }
1535}
1536"#,
1537 );
1538 }
1539 } 1536 }
1540} 1537}
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs
index 6dc862826..e77a20fea 100644
--- a/crates/hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs
@@ -11,10 +11,7 @@ use hir_def::{
11}; 11};
12use hir_expand::diagnostics::DiagnosticSink; 12use hir_expand::diagnostics::DiagnosticSink;
13 13
14use crate::{ 14use crate::{db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Ty};
15 db::HirDatabase, diagnostics::MissingUnsafe, lower::CallableDefId, ApplicationTy,
16 InferenceResult, Ty, TypeCtor,
17};
18 15
19pub(super) struct UnsafeValidator<'a, 'b: 'a> { 16pub(super) struct UnsafeValidator<'a, 'b: 'a> {
20 owner: DefWithBodyId, 17 owner: DefWithBodyId,
@@ -87,13 +84,8 @@ fn walk_unsafe(
87) { 84) {
88 let expr = &body.exprs[current]; 85 let expr = &body.exprs[current];
89 match expr { 86 match expr {
90 Expr::Call { callee, .. } => { 87 &Expr::Call { callee, .. } => {
91 let ty = &infer[*callee]; 88 if let Some(func) = infer[callee].as_fn_def() {
92 if let &Ty::Apply(ApplicationTy {
93 ctor: TypeCtor::FnDef(CallableDefId::FunctionId(func)),
94 ..
95 }) = ty
96 {
97 if db.function_data(func).is_unsafe { 89 if db.function_data(func).is_unsafe {
98 unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); 90 unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
99 } 91 }
@@ -118,7 +110,7 @@ fn walk_unsafe(
118 } 110 }
119 } 111 }
120 Expr::UnaryOp { expr, op: UnaryOp::Deref } => { 112 Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
121 if let Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }) = &infer[*expr] { 113 if let Ty::Raw(..) = &infer[*expr] {
122 unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); 114 unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
123 } 115 }
124 } 116 }