aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir_ty/src/diagnostics.rs4
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs145
-rw-r--r--editors/code/src/tasks.ts12
3 files changed, 43 insertions, 118 deletions
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index 7598e2193..283894704 100644
--- a/crates/hir_ty/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics.rs
@@ -349,11 +349,11 @@ impl fmt::Display for CaseType {
349 349
350#[derive(Debug)] 350#[derive(Debug)]
351pub enum IdentType { 351pub enum IdentType {
352 Argument,
353 Constant, 352 Constant,
354 Enum, 353 Enum,
355 Field, 354 Field,
356 Function, 355 Function,
356 Parameter,
357 StaticVariable, 357 StaticVariable,
358 Structure, 358 Structure,
359 Variable, 359 Variable,
@@ -363,11 +363,11 @@ pub enum IdentType {
363impl fmt::Display for IdentType { 363impl fmt::Display for IdentType {
364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365 let repr = match self { 365 let repr = match self {
366 IdentType::Argument => "Argument",
367 IdentType::Constant => "Constant", 366 IdentType::Constant => "Constant",
368 IdentType::Enum => "Enum", 367 IdentType::Enum => "Enum",
369 IdentType::Field => "Field", 368 IdentType::Field => "Field",
370 IdentType::Function => "Function", 369 IdentType::Function => "Function",
370 IdentType::Parameter => "Parameter",
371 IdentType::StaticVariable => "Static variable", 371 IdentType::StaticVariable => "Static variable",
372 IdentType::Structure => "Structure", 372 IdentType::Structure => "Structure",
373 IdentType::Variable => "Variable", 373 IdentType::Variable => "Variable",
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index ef982cbcd..cfb5d7320 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::Parameter } 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 }
@@ -723,10 +643,10 @@ fn NonSnakeCaseName() {}
723 check_diagnostics( 643 check_diagnostics(
724 r#" 644 r#"
725fn foo(SomeParam: u8) {} 645fn foo(SomeParam: u8) {}
726 // ^^^^^^^^^ Argument `SomeParam` should have snake_case name, e.g. `some_param` 646 // ^^^^^^^^^ Parameter `SomeParam` should have snake_case name, e.g. `some_param`
727 647
728fn foo2(ok_param: &str, CAPS_PARAM: u8) {} 648fn foo2(ok_param: &str, CAPS_PARAM: u8) {}
729 // ^^^^^^^^^^ Argument `CAPS_PARAM` should have snake_case name, e.g. `caps_param` 649 // ^^^^^^^^^^ Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param`
730"#, 650"#,
731 ); 651 );
732 } 652 }
@@ -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}
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts
index 694ee1e41..947b3f2e4 100644
--- a/editors/code/src/tasks.ts
+++ b/editors/code/src/tasks.ts
@@ -80,7 +80,7 @@ export async function buildCargoTask(
80 throwOnError: boolean = false 80 throwOnError: boolean = false
81): Promise<vscode.Task> { 81): Promise<vscode.Task> {
82 82
83 let exec: vscode.ShellExecution | undefined = undefined; 83 let exec: vscode.ProcessExecution | vscode.ShellExecution | undefined = undefined;
84 84
85 if (customRunner) { 85 if (customRunner) {
86 const runnerCommand = `${customRunner}.buildShellExecution`; 86 const runnerCommand = `${customRunner}.buildShellExecution`;
@@ -105,13 +105,13 @@ export async function buildCargoTask(
105 105
106 if (!exec) { 106 if (!exec) {
107 // Check whether we must use a user-defined substitute for cargo. 107 // Check whether we must use a user-defined substitute for cargo.
108 const cargoCommand = definition.overrideCargo ? definition.overrideCargo : toolchain.cargoPath(); 108 // Split on spaces to allow overrides like "wrapper cargo".
109 const overrideCargo = definition.overrideCargo ?? definition.overrideCargo;
110 const cargoCommand = overrideCargo?.split(" ") ?? [toolchain.cargoPath()];
109 111
110 // Prepare the whole command as one line. It is required if user has provided override command which contains spaces, 112 const fullCommand = [...cargoCommand, ...args];
111 // for example "wrapper cargo". Without manual preparation the overridden command will be quoted and fail to execute.
112 const fullCommand = [cargoCommand, ...args].join(" ");
113 113
114 exec = new vscode.ShellExecution(fullCommand, definition); 114 exec = new vscode.ProcessExecution(fullCommand[0], fullCommand.slice(1), definition);
115 } 115 }
116 116
117 return new vscode.Task( 117 return new vscode.Task(