diff options
Diffstat (limited to 'crates/hir_ty/src/diagnostics')
-rw-r--r-- | crates/hir_ty/src/diagnostics/decl_check.rs | 279 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/decl_check/case_conv.rs | 34 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/expr.rs | 96 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/match_check.rs | 37 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/unsafe_check.rs | 16 |
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 | }; |
26 | use stdx::{always, never}; | ||
26 | use syntax::{ | 27 | use 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 | ||
32 | use crate::{ | 33 | use 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 | ||
37 | mod allow { | 38 | mod 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 | ||
43 | pub(super) struct DeclValidator<'a, 'b: 'a> { | 44 | pub(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(¶m_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(), ¶m_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 | ||
699 | fn 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 | |||
707 | fn 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)] |
716 | mod tests { | 675 | mod 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. |
9 | pub(crate) fn to_camel_case(ident: &str) -> Option<String> { | 9 | pub(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 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_def::{expr::Statement, path::path, resolver::HasResolver, AdtId, DefWithBodyId}; | 5 | use hir_def::{ |
6 | use hir_expand::diagnostics::DiagnosticSink; | 6 | expr::Statement, path::path, resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, |
7 | }; | ||
8 | use hir_expand::{diagnostics::DiagnosticSink, name}; | ||
7 | use rustc_hash::FxHashSet; | 9 | use rustc_hash::FxHashSet; |
8 | use syntax::{ast, AstPtr}; | 10 | use 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 | ||
21 | pub(crate) use hir_def::{ | 23 | pub(crate) use hir_def::{ |
@@ -24,6 +26,8 @@ pub(crate) use hir_def::{ | |||
24 | LocalFieldId, VariantId, | 26 | LocalFieldId, VariantId, |
25 | }; | 27 | }; |
26 | 28 | ||
29 | use super::ReplaceFilterMapNextWithFindMap; | ||
30 | |||
27 | pub(super) struct ExprValidator<'a, 'b: 'a> { | 31 | pub(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::{ | |||
227 | use la_arena::Idx; | 227 | use la_arena::Idx; |
228 | use smallvec::{smallvec, SmallVec}; | 228 | use smallvec::{smallvec, SmallVec}; |
229 | 229 | ||
230 | use crate::{db::HirDatabase, ApplicationTy, InferenceResult, Ty, TypeCtor}; | 230 | use 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#" | ||
1500 | fn 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#" | ||
1530 | fn 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 | }; |
12 | use hir_expand::diagnostics::DiagnosticSink; | 12 | use hir_expand::diagnostics::DiagnosticSink; |
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Ty}; |
15 | db::HirDatabase, diagnostics::MissingUnsafe, lower::CallableDefId, ApplicationTy, | ||
16 | InferenceResult, Ty, TypeCtor, | ||
17 | }; | ||
18 | 15 | ||
19 | pub(super) struct UnsafeValidator<'a, 'b: 'a> { | 16 | pub(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 | } |