aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-05-31 17:06:40 +0100
committerAleksey Kladov <[email protected]>2021-05-31 17:45:50 +0100
commit341f8bb200d60caf4c0ea70738198ac8d62218b8 (patch)
tree17b534d34bf7f6da4f550678374d7da98d49c687 /crates
parent10b15b27f2f4bee6de63f66c344f741482becd21 (diff)
fix: avoid panics in match case diagnostic
Diffstat (limited to 'crates')
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs141
1 files changed, 33 insertions, 108 deletions
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index ef982cbcd..bfa53bdce 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -150,29 +150,11 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
150 expected_case: CaseType::LowerSnakeCase, 150 expected_case: CaseType::LowerSnakeCase,
151 }); 151 });
152 152
153 // Check the param names.
154 let fn_param_replacements = body
155 .params
156 .iter()
157 .filter_map(|&id| match &body[id] {
158 Pat::Bind { name, .. } => Some(name),
159 _ => None,
160 })
161 .filter_map(|param_name| {
162 Some(Replacement {
163 current_name: param_name.clone(),
164 suggested_text: to_lower_snake_case(&param_name.to_string())?,
165 expected_case: CaseType::LowerSnakeCase,
166 })
167 })
168 .collect();
169
170 // Check the patterns inside the function body. 153 // Check the patterns inside the function body.
154 // This includes function parameters.
171 let pats_replacements = body 155 let pats_replacements = body
172 .pats 156 .pats
173 .iter() 157 .iter()
174 // We aren't interested in function parameters, we've processed them above.
175 .filter(|(pat_idx, _)| !body.params.contains(&pat_idx))
176 .filter_map(|(id, pat)| match pat { 158 .filter_map(|(id, pat)| match pat {
177 Pat::Bind { name, .. } => Some((id, name)), 159 Pat::Bind { name, .. } => Some((id, name)),
178 _ => None, 160 _ => None,
@@ -190,11 +172,10 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
190 .collect(); 172 .collect();
191 173
192 // If there is at least one element to spawn a warning on, go to the source map and generate a warning. 174 // If there is at least one element to spawn a warning on, go to the source map and generate a warning.
193 self.create_incorrect_case_diagnostic_for_func( 175 if let Some(fn_name_replacement) = fn_name_replacement {
194 func, 176 self.create_incorrect_case_diagnostic_for_func(func, fn_name_replacement);
195 fn_name_replacement, 177 }
196 fn_param_replacements, 178
197 );
198 self.create_incorrect_case_diagnostic_for_variables(func, pats_replacements); 179 self.create_incorrect_case_diagnostic_for_variables(func, pats_replacements);
199 } 180 }
200 181
@@ -203,100 +184,34 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
203 fn create_incorrect_case_diagnostic_for_func( 184 fn create_incorrect_case_diagnostic_for_func(
204 &mut self, 185 &mut self,
205 func: FunctionId, 186 func: FunctionId,
206 fn_name_replacement: Option<Replacement>, 187 fn_name_replacement: Replacement,
207 fn_param_replacements: Vec<Replacement>,
208 ) { 188 ) {
209 // XXX: only look at sources if we do have incorrect names
210 if fn_name_replacement.is_none() && fn_param_replacements.is_empty() {
211 return;
212 }
213
214 let fn_loc = func.lookup(self.db.upcast()); 189 let fn_loc = func.lookup(self.db.upcast());
215 let fn_src = fn_loc.source(self.db.upcast()); 190 let fn_src = fn_loc.source(self.db.upcast());
216 191
217 // Diagnostic for function name. 192 // Diagnostic for function name.
218 if let Some(replacement) = fn_name_replacement { 193 let ast_ptr = match fn_src.value.name() {
219 let ast_ptr = match fn_src.value.name() { 194 Some(name) => name,
220 Some(name) => name,
221 None => {
222 never!(
223 "Replacement ({:?}) was generated for a function without a name: {:?}",
224 replacement,
225 fn_src
226 );
227 return;
228 }
229 };
230
231 let diagnostic = IncorrectCase {
232 file: fn_src.file_id,
233 ident_type: IdentType::Function,
234 ident: AstPtr::new(&ast_ptr),
235 expected_case: replacement.expected_case,
236 ident_text: replacement.current_name.to_string(),
237 suggested_text: replacement.suggested_text,
238 };
239
240 self.sink.push(diagnostic);
241 }
242
243 // Diagnostics for function params.
244 let fn_params_list = match fn_src.value.param_list() {
245 Some(params) => params,
246 None => { 195 None => {
247 always!( 196 never!(
248 fn_param_replacements.is_empty(), 197 "Replacement ({:?}) was generated for a function without a name: {:?}",
249 "Replacements ({:?}) were generated for a function parameters which had no parameters list: {:?}", 198 fn_name_replacement,
250 fn_param_replacements,
251 fn_src 199 fn_src
252 ); 200 );
253 return; 201 return;
254 } 202 }
255 }; 203 };
256 let mut fn_params_iter = fn_params_list.params();
257 for param_to_rename in fn_param_replacements {
258 // We assume that parameters in replacement are in the same order as in the
259 // actual params list, but just some of them (ones that named correctly) are skipped.
260 let ast_ptr: ast::Name = loop {
261 match fn_params_iter.next() {
262 Some(element) => {
263 if let Some(ast::Pat::IdentPat(pat)) = element.pat() {
264 if pat.to_string() == param_to_rename.current_name.to_string() {
265 if let Some(name) = pat.name() {
266 break name;
267 }
268 // This is critical. If we consider this parameter the expected one,
269 // it **must** have a name.
270 never!(
271 "Pattern {:?} equals to expected replacement {:?}, but has no name",
272 element,
273 param_to_rename
274 );
275 return;
276 }
277 }
278 }
279 None => {
280 never!(
281 "Replacement ({:?}) was generated for a function parameter which was not found: {:?}",
282 param_to_rename, fn_src
283 );
284 return;
285 }
286 }
287 };
288 204
289 let diagnostic = IncorrectCase { 205 let diagnostic = IncorrectCase {
290 file: fn_src.file_id, 206 file: fn_src.file_id,
291 ident_type: IdentType::Argument, 207 ident_type: IdentType::Function,
292 ident: AstPtr::new(&ast_ptr), 208 ident: AstPtr::new(&ast_ptr),
293 expected_case: param_to_rename.expected_case, 209 expected_case: fn_name_replacement.expected_case,
294 ident_text: param_to_rename.current_name.to_string(), 210 ident_text: fn_name_replacement.current_name.to_string(),
295 suggested_text: param_to_rename.suggested_text, 211 suggested_text: fn_name_replacement.suggested_text,
296 }; 212 };
297 213
298 self.sink.push(diagnostic); 214 self.sink.push(diagnostic);
299 }
300 } 215 }
301 216
302 /// Given the information about incorrect variable names, looks up into the source code 217 /// Given the information about incorrect variable names, looks up into the source code
@@ -327,20 +242,25 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
327 None => continue, 242 None => continue,
328 }; 243 };
329 244
245 let is_param = ast::Param::can_cast(parent.kind());
246
330 // We have to check that it's either `let var = ...` or `var @ Variant(_)` statement, 247 // We have to check that it's either `let var = ...` or `var @ Variant(_)` statement,
331 // because e.g. match arms are patterns as well. 248 // because e.g. match arms are patterns as well.
332 // In other words, we check that it's a named variable binding. 249 // In other words, we check that it's a named variable binding.
333 let is_binding = ast::LetStmt::can_cast(parent.kind()) 250 let is_binding = ast::LetStmt::can_cast(parent.kind())
334 || (ast::MatchArm::can_cast(parent.kind()) 251 || (ast::MatchArm::can_cast(parent.kind())
335 && ident_pat.at_token().is_some()); 252 && ident_pat.at_token().is_some());
336 if !is_binding { 253 if !(is_param || is_binding) {
337 // This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm. 254 // This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm.
338 continue; 255 continue;
339 } 256 }
340 257
258 let ident_type =
259 if is_param { IdentType::Argument } else { IdentType::Variable };
260
341 let diagnostic = IncorrectCase { 261 let diagnostic = IncorrectCase {
342 file: source_ptr.file_id, 262 file: source_ptr.file_id,
343 ident_type: IdentType::Variable, 263 ident_type,
344 ident: AstPtr::new(&name_ast), 264 ident: AstPtr::new(&name_ast),
345 expected_case: replacement.expected_case, 265 expected_case: replacement.expected_case,
346 ident_text: replacement.current_name.to_string(), 266 ident_text: replacement.current_name.to_string(),
@@ -408,7 +328,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
408 struct_name_replacement: Option<Replacement>, 328 struct_name_replacement: Option<Replacement>,
409 struct_fields_replacements: Vec<Replacement>, 329 struct_fields_replacements: Vec<Replacement>,
410 ) { 330 ) {
411 // XXX: only look at sources if we do have incorrect names 331 // XXX: Only look at sources if we do have incorrect names.
412 if struct_name_replacement.is_none() && struct_fields_replacements.is_empty() { 332 if struct_name_replacement.is_none() && struct_fields_replacements.is_empty() {
413 return; 333 return;
414 } 334 }
@@ -1037,4 +957,9 @@ fn qualify() {
1037 "#, 957 "#,
1038 ) 958 )
1039 } 959 }
960
961 #[test] // Issue #8809.
962 fn parenthesized_parameter() {
963 check_diagnostics(r#"fn f((O): _) {}"#)
964 }
1040} 965}